Skip to content

Commit

Permalink
Completely redo how plugin messages are used, switch to fabric packets
Browse files Browse the repository at this point in the history
  • Loading branch information
Aeltumn committed Aug 6, 2023
1 parent 419ac19 commit 6444941
Show file tree
Hide file tree
Showing 14 changed files with 576 additions and 107 deletions.
49 changes: 12 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,54 +79,29 @@ Alternatively, if you cannot provide the uuid and wish to provide a texture dire
```java
player.sendMessage(Component.translatable("%nox_uuid%3e7a89ee-c4e2-4392-a317-444b861b0794,false,0,0,1.0","This is shown for non-Noxesium clients"));
```

or

```java
player.sendMessage(Component.translatable("%nox_raw%ewogICJ0aW1lc3RhbXAiIDogMTYxMjA1MTQxNDA4NywKICAicHJvZmlsZUlkIiA6ICJhNzdkNmQ2YmFjOWE0NzY3YTFhNzU1NjYxOTllYmY5MiIsCiAgInByb2ZpbGVOYW1lIiA6ICIwOEJFRDUiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTkyNjZiYThiY2Q4ZmE2NGE0NjgyOGY1NjEwZDk5MGE1MzEzMzVmNjQzZWYzOWYzZDA1ZDdmZTFjMWVkYjg4IiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=,false,0,0,1.0","This is shown for non-Noxesium clients"));
```

## Message Channels

Noxesium communicates with the server over plugin messaging channels. The namespace of these channels matches the current API version of Noxesium, which is currently `v1`. Currently, there are 4 messaging channels available.

`noxesium-v1:client_information` (client -> server); Contains the Protocol version of the mod version used by the client. This is sent right after a client joins the server. If a proxy server is used it's recommended to cache this value per
session on the server-side. Protocol versions are bumped when major new capabilities are introduced so servers can disable features for outdated mod users. E.g. the introduction of player head components caused a protocol bump so MCC Island
can disable the setting for users with an older mod version.

| Field Name | Field Type | Notes |
|------------------|------------|-------------|
| Protocol Version | VarInt | Currently 2 |

`noxesium-v1:client_settings` (client -> server); Contains information about the settings used by the client. Sent when a player joins a server or whenever the player changes their settings.

| Field Name | Field Type | Notes |
|---------------------------|------------|-----------------------------------------------------|
| GUI Scale | VarInt | The value set in the video settings screen. |
| Internal GUI Scale | Double | The internal GUI scale value. |
| Scaled Width | VarInt | The scaled width of the window. |
| Scaled Height | VarInt | The scaled height of the window. |
| Enforce Unicode | Boolean | Whether the enforce unicode setting is on. |
| Touchscreen Mode | Boolean | Whether touchscreen mode is on. |
| Notification Display Time | Double | The value of the notification display time setting. |

`noxesium-v1:server_rules` (server -> client); Can be used to modify the current values of server rules known to this client. You should not mark rules being changed for reset, the client avoids triggering changes if a rule's value did not
change.

| Field Name | Field Type | Notes |
|-----------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Rules to reset | VarInt Array | An array of rule ids to reset to their default value. |
| Amount of rules | VarInt | Size of the rules array. |
| Rules to change | ServerRule Array | An array of server rules to change, each rule has their own data format. This object always starts with a VarInt of the rule index before each rule's own data. |

`noxesium-v1:reset` (server -> client); Allows the server to reset specific parts of client data.
Noxesium communicates with the server over the plugin messaging channel, but through the use of Fabric Packets. This implementation means packets are defined in Noxesium's code as they would be in Minecrafts source code. Clientbound and
serverbound packets are implemented separately since both only need to be handled on the client-side. Noxesium sends and receives all packets on the `noxesium-v1` namespace. This version identifier is not intended to change, it has changed
once for Noxesium 1.0.0 but should not change again unless the entire packet system is redone. Instead, each packet starts with its own version integer. This version integer can be used to change individual packets. As Noxesium is intended
to be used between clients on servers on both different Minecraft versions and different Noxesium versions it is necessary to strongly support backwards compatibility. For this purpose the version integer in each packet can be
individually tweaked. Packets will both attempt to send in an older format and parse from an older format if the server is outdated. It is left up to the developer to build similar compatibility on the server-side.

| Field Name | Field Type | Notes |
|------------|------------|------------------------------------------------------------------------------------------------------------|
| Command | Byte | Bitmasked command byte. 0x01 clears all server rule settings. 0x02 clears all cached custom player skulls. |
If you are interested in understanding what packets Noxesium can receive and which it can send, feel free to look through the `network.clientbound` and `network.serverbound` packages. Packets are documented in their respective classes and
should offer insights into their capabilities through the variable names and constructor arguments.

## Server Rules

Server Rules are a system similar to Game Rules but able to be modified whenever desired. Server Rule settings are cleared whenever a player disconnects from a server. These values allow a server to affect the client's state easily. Below
is a list of every server rule currently available and their data format in the plugin message:
Server Rules are a special system similar to Game Rules but able to be modified whenever desired. Server Rule settings are cleared whenever a player disconnects from a server. These values allow a server to affect the client's state easily.
Below is a list of every server rule currently available and their data format. While server rules have packets for setting each individual server rule can decide how the packet is decoded. As such, a detailed breakdown is provided for each
individual rule and how it interprets the incoming packet.

**Disable Auto Spin Attack**. Disables colliding with other entities while riptiding. This also prevents the spin attack from dealing any damage to entities moved through.

Expand Down
33 changes: 33 additions & 0 deletions api/src/main/java/com/noxcrew/noxesium/api/util/ByteUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.noxcrew.noxesium.api.util;

/**
* Provides utilities for storing additional details in a single byte value.
*/
public class ByteUtil {

/**
* Returns whether the flag at index in command is enabled.
*
* @param command A byte that contains multiple boolean values.
* @param index The index in the byte to read out.
*/
public static boolean hasFlag(byte command, int index) {
return (command & (1 << index)) != 0;
}

/**
* Returns an edited version of the command to have the value at
* the given index set to value.
*
* @param command A byte that contains multiple boolean values.
* @param index The index in the byte to write to.
* @param value The new value to store in the byte.
*/
public static byte setFlag(byte command, int index, boolean value) {
if (value) {
return (byte) (command | (1 << index));
} else {
return (byte) (command & ~(1 << index));
}
}
}
68 changes: 18 additions & 50 deletions fabric/src/main/java/com/noxcrew/noxesium/NoxesiumMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import com.noxcrew.noxesium.feature.rule.ServerRule;
import com.noxcrew.noxesium.feature.skull.CustomSkullFont;
import com.noxcrew.noxesium.network.NoxesiumPackets;
import com.noxcrew.noxesium.network.serverbound.ServerboundClientInformationPacket;
import com.noxcrew.noxesium.network.serverbound.ServerboundClientSettingsPacket;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;

/**
* The main file for the client-side implementation of Noxesium.
Expand All @@ -22,14 +22,6 @@ public class NoxesiumMod implements ClientModInitializer {
*/
public static final int VERSION = 3;

public static final String API_VERSION = "v1";
public static final String API_NAMESPACE = "noxesium-" + API_VERSION;

public static final ResourceLocation CLIENT_INFORMATION_CHANNEL = new ResourceLocation(API_NAMESPACE, "client_information");
public static final ResourceLocation CLIENT_SETTINGS_CHANNEL = new ResourceLocation(API_NAMESPACE, "client_settings");
public static final ResourceLocation SERVER_RULE_CHANNEL = new ResourceLocation(API_NAMESPACE, "server_rules");
public static final ResourceLocation RESET_CHANNEL = new ResourceLocation(API_NAMESPACE, "reset");

public static final String BUKKIT_COMPOUND_ID = "PublicBukkitValues";
public static final String NOXESIUM_PREFIX = "noxesium";
public static final String IMMOVABLE_TAG = NOXESIUM_PREFIX + ":immovable";
Expand All @@ -44,31 +36,10 @@ public void onInitializeClient() {
// Every time the client joins a server we send over information on the version being used
ClientPlayConnectionEvents.JOIN.register((ignored1, ignored2, ignored3) -> {
// Send a packet containing information about the client version of Noxesium
{
if (Minecraft.getInstance().getConnection() != null) {
var outBuffer = PacketByteBufs.create();
outBuffer.writeVarInt(VERSION);
ClientPlayNetworking.send(CLIENT_INFORMATION_CHANNEL, outBuffer);
}
if (Minecraft.getInstance().getConnection() != null) {
new ServerboundClientInformationPacket(VERSION).send();
}

// Set up a receiver for any server rules
ClientPlayNetworking.registerReceiver(SERVER_RULE_CHANNEL, (client, handler, buffer, responseSender) -> {
ServerRule.readAll(buffer);
});

// Set up a receiver for clearing client cached data
ClientPlayNetworking.registerReceiver(RESET_CHANNEL, (client, handler, buffer, responseSender) -> {
var command = buffer.readByte();

if (hasFlag(command, 0)) {
ServerRule.clearAll();
}
if (hasFlag(command, 1)) {
CustomSkullFont.resetCaches();
}
});

// Inform the player about the GUI scale of the client
syncGuiScale();
});
Expand All @@ -83,33 +54,30 @@ public void onInitializeClient() {
// any components that persist between before/after this point
CustomSkullFont.clearCaches();
});
}

/**
* Returns whether the flag at index in command is enabled.
*/
private boolean hasFlag(byte command, int index) {
return (command & (1 << index)) != 0;
// Register all universal messaging channels
NoxesiumPackets.registerPackets("universal");
}

/**
* Sends a packet to the server containing the GUI scale of the client which
* allows servers to more accurately adapt their UI to clients.
*/
public static void syncGuiScale() {
// Don't send if there is no established connection
if (Minecraft.getInstance().getConnection() == null) return;
var outBuffer = PacketByteBufs.create();

var window = Minecraft.getInstance().getWindow();
var options = Minecraft.getInstance().options;

outBuffer.writeVarInt(options.guiScale().get());
outBuffer.writeDouble(window.getGuiScale());
outBuffer.writeVarInt(window.getGuiScaledWidth());
outBuffer.writeVarInt(window.getGuiScaledHeight());
outBuffer.writeBoolean(Minecraft.getInstance().isEnforceUnicode());
outBuffer.writeBoolean(options.touchscreen().get());
outBuffer.writeDouble(options.notificationDisplayTime().get());

ClientPlayNetworking.send(CLIENT_SETTINGS_CHANNEL, outBuffer);
new ServerboundClientSettingsPacket(
options.guiScale().get(),
window.getGuiScale(),
window.getGuiScaledWidth(),
window.getGuiScaledHeight(),
Minecraft.getInstance().isEnforceUnicode(),
options.touchscreen().get(),
options.notificationDisplayTime().get()
).send();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.noxcrew.noxesium.feature.rule;

import com.google.common.base.Preconditions;
import net.minecraft.network.FriendlyByteBuf;

import java.util.HashMap;
Expand All @@ -16,7 +17,7 @@ public abstract class ServerRule<T> {

public ServerRule(int index) {
// Add any created server rule to the global list
rules.put(index, this);
register(index, this);
}

/**
Expand All @@ -39,7 +40,7 @@ public T get() {
/**
* Resets the value of this rule.
*/
private void reset() {
public void reset() {
var oldValue = value;
value = getDefault();

Expand All @@ -51,7 +52,7 @@ private void reset() {
/**
* Reads the value of this rule from the given [buffer].
*/
private void set(FriendlyByteBuf buffer) {
public void set(FriendlyByteBuf buffer) {
var oldValue = value;
value = read(buffer);

Expand All @@ -66,26 +67,23 @@ private void set(FriendlyByteBuf buffer) {
protected void onValueChanged(T oldValue, T newValue) {
}


/**
* Reads the data for all rules from the server.
* Registers a new server rule with the given index and data.
*
* @param index The index of this rule, must be unique.
* @param rule The object with the data for this rule.
*/
public static void readAll(FriendlyByteBuf buffer) {
// First we get a list of ints to clear the data for
var clear = buffer.readVarIntArray();
for (var index : clear) {
var rule = rules.get(index);
if (rule == null) continue;
rule.reset();
}
public static void register(int index, ServerRule<?> rule) {
Preconditions.checkArgument(!rules.containsKey(index), "Index " + index + " was used by multiple server rules");
rules.put(index, rule);
}

// Second we let each value read the data from the buffer
var amount = buffer.readVarInt();
for (int i = 0; i < amount; i++) {
var index = buffer.readVarInt();
var rule = rules.get(index);
if (rule == null) continue;
rule.set(buffer);
}
/**
* Returns the rule saved under the given index.
*/
public static ServerRule<?> getIndex(int index) {
return rules.get(index);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.noxcrew.noxesium.network;

import net.fabricmc.fabric.api.networking.v1.FabricPacket;

/**
* The basis for a fabric packet as used by Noxesium, these always contain
* a version varint which allows different versions of the mod to communicate.
*/
public abstract class NoxesiumPacket implements FabricPacket {

/** The version of this packet. */
public final int version;

/**
* Creates a new Noxesium packet with the given version.
*
* @param version The version of this packet, this is always
* the first varint of any Noxesium packet and
* allows the contents of packets to change
* over time without much issue.
*/
public NoxesiumPacket(int version) {
this.version = version;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.noxcrew.noxesium.network;

import com.noxcrew.noxesium.network.clientbound.ClientboundNoxesiumPacket;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.player.LocalPlayer;

/**
* A simple packet handler used by Noxesium which defers the result of the packet
* to the packet instance itself.
*/
public class NoxesiumPacketHandler implements ClientPlayNetworking.PlayPacketHandler<FabricPacket> {

@Override
public void receive(FabricPacket packet, LocalPlayer player, PacketSender responseSender) {
if (packet instanceof ClientboundNoxesiumPacket clientboundNoxesiumPacket) {
clientboundNoxesiumPacket.receive(player, responseSender);
}
}
}
Loading

0 comments on commit 6444941

Please sign in to comment.