GrieferGames

GrieferGames Wiki

Seite wird geladen...

1.8 / Funktionen / 🔎 Tutorials / FÃŒr Entwickler / Client Payloads

Client Payloads

GrieferGames sendet sogenannte Custom Plugin Messages (Client Payloads) ÃŒber einen dedizierten Kanal an verbundene Clients. Diese Seite dokumentiert alle verfÃŒgbaren Payloads, ihr binÀres Übertragungsformat und wie du sie in deinem Mod empfangen kannst.

#Übersicht

EigenschaftWert
Kanalgriefergames:main
RichtungServer → Client (unidirektional)
RegistrierungKeine client-seitige Registrierung erforderlich

Du musst kein Registrierungs- oder Handshake-Paket an den Server senden. Der Server schickt die Payloads eigenstÀndig an jeden verbundenen Client.

#VerfÃŒgbarkeit

UmgebungWo werden Payloads gesendet?
1.8Auf allen Servern außer der Lobby und dem Portalraum
CloudAlle Server auf denen die Verwendung von Geld möglich ist

#Übertragungsformat

Alle Payloads teilen dieselbe Àußere Struktur auf dem griefergames:main-Channel:

┌──────────────────────────────────┐
│  id        UTF (Java DataOutput) │      ← Payload-Identifikator
│  <body>    payload-spezifische   │      ← Felder je nach Payload (siehe unten)
│            Daten                 │
└──────────────────────────────────┘

Der UTF-String wird im Java-DataOutputStream.writeUTF-Format kodiert:

  • 2 Bytes Big-Endian Unsigned Short → LÀnge der folgenden UTF-8-Byte-Sequenz
  • N Bytes → String-Inhalt in modifiziertem UTF-8

Alle nachfolgenden numerischen Felder folgen der Standard-Big-Endian-Kodierung von DataOutputStream.

#VerfÃŒgbare Payloads

#accountbalance

Wird an den Client gesendet, sobald sich das Bargeld des Spielers Àndert, sowie einmalig beim Betreten eines Servers.

FeldTypBeschreibung
idUTF"accountbalance"
balancedouble (8 Bytes, Big-Endian IEEE 754)Aktuelles Guthaben des Spielers, auf 2 Dezimalstellen gerundet

Beispiel-Hex-AufschlÃŒsselung (Guthaben = 1234.56):

00 0D 61 63 63 6F 75 6E 74 62 61 6C 61 6E 63 65   ← UTF: 2-Byte-LÀnge (13) + "accountbalance"
40 93 4A 3D 70 A3 D7 0A                           ← double: 1234.56

#bankbalance

Wird an den Client gesendet, sobald sich das Bankguthaben des Spielers Àndert, sowie einmalig beim Betreten eines Servers.

FeldTypBeschreibung
idUTF"bankbalance"
balancedouble (8 Bytes, Big-Endian IEEE 754)Aktuelles Bankguthaben des Spielers, auf 2 Dezimalstellen gerundet

#blockoftheday

Wird an den Client gesendet, um Informationen ÃŒber den Block oder die Entity des Tages zu ÃŒbermitteln.

FeldTypBeschreibung
idUTF"blockoftheday"
typeUTF"BLOCK", "MATERIAL" oder "ENTITY"
blockMaterialUTFMaterialname (z.B. "DIAMOND_ORE"); leer wenn type = "ENTITY"
blockDataint (4 Bytes)Block-Daten / Varianten; immer 0
entityTypeUTFEntity-Typ (z.B. "WITHER"); leer wenn type = "BLOCK" oder "MATERIAL"

Feldlogik je nach Type:

  • type = "BLOCK" oder "MATERIAL": blockMaterial gefÃŒllt, entityType leer
  • type = "ENTITY": entityType gefÃŒllt, blockMaterial leer

#blockoftheday_progress

Wird an den Client gesendet, um den aktuellen Fortschritt beim Block des Tages zu synchronisieren. Dieses Payload hat keine zusÀtzlichen Felder.

FeldTypBeschreibung
idUTF"blockoftheday_progress"

#booster

Wird an den Client gesendet, um aktive Booster und deren Multiplikatoren zu ÃŒbermitteln.

FeldTypBeschreibung
idUTF"booster"
countint (4 Bytes)Anzahl der Booster in dieser Liste
FÃŒr jeden Booster:
typeUTFBooster-Typ: "BREAK", "DROP", "FLY", "MOB", "XP"
multiplierint (4 Bytes)Multiplikator des Boosters (z.B. 2 fÃŒr 2x)

#clearlag

Wird an den Client gesendet, um die verbleibenden Sekunden bis zum nÀchsten ClearLag-Event zu Ìbermitteln.

FeldTypBeschreibung
idUTF"clearlag"
remainingSecondslong (8 Bytes)Verbleibende Sekunden bis zum nÀchsten ClearLag

#entityremover

Wird an den Client gesendet, um die verbleibenden Sekunden bis zum nÀchsten Entity Remover-Event zu Ìbermitteln.

FeldTypBeschreibung
idUTF"entityremover"
remainingSecondslong (8 Bytes)Verbleibende Sekunden bis zum nÀchsten Entity Remover

#plotchat_configuration

Wird an den Client gesendet, um die PlotChat-Konfiguration zu aktualisieren (ob PlotChat aktiviert ist oder nicht).

FeldTypBeschreibung
idUTF"plotchat_configuration"
statusboolean (1 Byte)true = PlotChat aktiviert, false = deaktiviert

#Payloads empfangen — Code-Beispiele

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;

public class MeinModClient implements ClientModInitializer {

    public static final Identifier CHANNEL_ID = Identifier.of("griefergames", "main");

    public record GrieferGamesPayload(byte[] data) implements CustomPayload {

        public static final CustomPayload.Id<GrieferGamesPayload> ID = new CustomPayload.Id<>(CHANNEL_ID);

        public static final PacketCodec<PacketByteBuf, GrieferGamesPayload> CODEC = PacketCodec.of(
                (payload, buf) -> buf.writeBytes(payload.data()),
                buf -> {
                    byte[] bytes = new byte[buf.readableBytes()];
                    buf.readBytes(bytes);
                    return new GrieferGamesPayload(bytes);
                }
        );

        @Override
        public Id<? extends CustomPayload> getId() {
            return ID;
        }
    }

    @Override
    public void onInitializeClient() {
        PayloadTypeRegistry.playS2C().register(GrieferGamesPayload.ID, GrieferGamesPayload.CODEC);
        ClientPlayNetworking.registerGlobalReceiver(GrieferGamesPayload.ID, (payload, context) -> {
            try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload.data()))) {
                String id = in.readUTF();

                switch (id) {
                    case "accountbalance": {
                        double balance = in.readDouble();
                        context.client().execute(() -> MeinMod.onMoneyUpdate(balance));
                        break;
                    }

                    case "bankbalance": {
                        double balance = in.readDouble();
                        context.client().execute(() -> MeinMod.onBankUpdate(balance));
                        break;
                    }

                    case "blockoftheday": {
                        String type = in.readUTF();
                        String blockMaterial = in.readUTF();
                        int blockData = in.readInt();
                        String entityType = in.readUTF();
                        
                        // type ist "BLOCK"/"MATERIAL" → blockMaterial gefÃŒllt, entityType leer
                        // type ist "ENTITY" → entityType gefÃŒllt, blockMaterial leer
                        context.client().execute(() -> MeinMod.onBlockOfTheDay(type, blockMaterial, blockData, entityType));
                        break;
                    }

                    case "blockoftheday_progress": {
                        context.client().execute(() -> MeinMod.onBlockProgressUpdate());
                        break;
                    }

                    case "booster": {
                        int count = in.readInt();
                        for (int i = 0; i < count; i++) {
                            String type = in.readUTF();
                            int multiplier = in.readInt();
                            context.client().execute(() -> MeinMod.onBooster(type, multiplier));
                        }
                        break;
                    }

                    case "clearlag": {
                        long remainingSeconds = in.readLong();
                        context.client().execute(() -> MeinMod.onClearLag(remainingSeconds));
                        break;
                    }

                    case "entityremover": {
                        long remainingSeconds = in.readLong();
                        context.client().execute(() -> MeinMod.onEntityRemover(remainingSeconds));
                        break;
                    }

                    case "plotchat_configuration": {
                        boolean status = in.readBoolean();
                        context.client().execute(() -> MeinMod.onPlotChatConfig(status));
                        break;
                    }

                    default:
                        // Unbekannte ID ignorieren
                        // Neue Payloads können jederzeit hinzukommen
                        break;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.network.FMLEventChannel;
import net.minecraftforge.fml.common.network.FMLNetworkEvent;
import net.minecraftforge.fml.common.network.NetworkRegistry;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;

@Mod(modid = "meinmod", name = "MeinMod", version = "1.0")
public class MeinMod {

   private static final String CHANNEL = "griefergames:main";

   @Mod.EventHandler
   public void init(FMLInitializationEvent event) {
      // Kanal registrieren und diese Klasse als Listener anmelden
      FMLEventChannel channel = NetworkRegistry.INSTANCE.newEventDrivenChannel(CHANNEL);
      channel.register(this);
   }

   @SubscribeEvent
   public void onCustomPacket(FMLNetworkEvent.ClientCustomPacketEvent event) {
      ByteBuf buf = event.getPacket().payload();
      byte[] data = new byte[buf.readableBytes()];
      buf.readBytes(data);

      try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(data))) {
         String id = in.readUTF();

         switch (id) {
            case "accountbalance": {
               double balance = in.readDouble();
               Minecraft.getMinecraft().addScheduledTask(() -> onMoneyUpdate(balance));
               break;
            }

            case "bankbalance": {
               double balance = in.readDouble();
               Minecraft.getMinecraft().addScheduledTask(() -> onBankUpdate(balance));
               break;
            }

            case "blockoftheday": {
               String type = in.readUTF();
               String blockMaterial = in.readUTF();
               int blockData = in.readInt();
               String entityType = in.readUTF();
               
               // type ist "BLOCK"/"MATERIAL" → blockMaterial gefÃŒllt, entityType leer
               // type ist "ENTITY" → entityType gefÃŒllt, blockMaterial leer
               Minecraft.getMinecraft().addScheduledTask(() -> onBlockOfTheDay(type, blockMaterial, blockData, entityType));
               break;
            }

            case "blockoftheday_progress": {
               Minecraft.getMinecraft().addScheduledTask(() -> onBlockProgressUpdate());
               break;
            }

            case "booster": {
               int count = in.readInt();
               for (int i = 0; i < count; i++) {
                  String type = in.readUTF();
                  int multiplier = in.readInt();
                  Minecraft.getMinecraft().addScheduledTask(() -> onBooster(type, multiplier));
               }
               break;
            }

            case "clearlag": {
               long remainingSeconds = in.readLong();
               Minecraft.getMinecraft().addScheduledTask(() -> onClearLag(remainingSeconds));
               break;
            }

            case "entityremover": {
               long remainingSeconds = in.readLong();
               Minecraft.getMinecraft().addScheduledTask(() -> onEntityRemover(remainingSeconds));
               break;
            }

            case "plotchat_configuration": {
               boolean status = in.readBoolean();
               Minecraft.getMinecraft().addScheduledTask(() -> onPlotChatConfig(status));
               break;
            }

            default:
               // Unbekannte ID ignorieren
               // Neue Payloads können jederzeit hinzukommen
               break;
         }

      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}
import net.labymod.api.client.component.event.PluginMessageEvent;
import net.labymod.api.event.Subscribe;

import java.io.DataInputStream;
import java.io.ByteArrayInputStream;

public class MeinPayloadListener {

    @Subscribe
    public void onPluginMessage(PluginMessageEvent event) {
        if (!"griefergames:main".equals(event.getIdentifier())) return;

        byte[] data = event.getData();
        try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(data))) {
            String id = in.readUTF();

            switch (id) {
                case "accountbalance": {
                    double balance = in.readDouble();
                    MeinAddon.onMoneyUpdate(balance);
                    break;
                }

                case "bankbalance": {
                    double balance = in.readDouble();
                    MeinAddon.onBankUpdate(balance);
                    break;
                }

                case "blockoftheday": {
                    String type = in.readUTF();
                    String blockMaterial = in.readUTF();
                    int blockData = in.readInt();
                    String entityType = in.readUTF();
                    
                    // type ist "BLOCK"/"MATERIAL" → blockMaterial gefÃŒllt, entityType leer
                    // type ist "ENTITY" → entityType gefÃŒllt, blockMaterial leer
                    MeinAddon.onBlockOfTheDay(type, blockMaterial, blockData, entityType);
                    break;
                }

                case "blockoftheday_progress": {
                    MeinAddon.onBlockProgressUpdate();
                    break;
                }

                case "booster": {
                    int count = in.readInt();
                    for (int i = 0; i < count; i++) {
                        String type = in.readUTF();
                        int multiplier = in.readInt();
                        MeinAddon.onBooster(type, multiplier);
                    }
                    break;
                }

                case "clearlag": {
                    long remainingSeconds = in.readLong();
                    MeinAddon.onClearLag(remainingSeconds);
                    break;
                }

                case "entityremover": {
                    long remainingSeconds = in.readLong();
                    MeinAddon.onEntityRemover(remainingSeconds);
                    break;
                }

                case "plotchat_configuration": {
                    boolean status = in.readBoolean();
                    MeinAddon.onPlotChatConfig(status);
                    break;
                }

                default:
                    // Unbekannte ID ignorieren
                    // Neue Payloads können jederzeit hinzukommen
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Den Listener im AddonEntryPoint registrieren:

labyAPI().eventBus().registerListener(new MeinPayloadListener());

Das Payload kommt als clientbound Plugin Message-Packet auf dem Channel griefergames:main an. Den Datenbereich mit einem DataInputStream lesen, wie in den Beispielen oben gezeigt.

ProtokollversionPacket-ID
1.80x3F
1.21+0x02 (Configuration) / 0x18 (Play)

#Zukunftssicherheit

Das Payload-System ist erweiterbar. Beim Empfang eines Payloads immer:

1

#Zuerst das id-Feld lesen.

2

#Per switch/if auf die ID reagieren und unbekannte IDs ignorieren.

Neue Payloads können jederzeit hinzukommen.

3

#Keine feste GesamtlÀnge der Packetdaten annehmen.

#KurzÃŒbersicht

Kanal:  griefergames:main
Payloads:
  ┌──────────────────────────────┬────────────────────────────┬──────────────────────────────────────────────┐
  │ ID                           │ Felder                     │ Wann gesendet                                │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ accountbalance               │ double                     │ Beim Betreten eines Servers & bei jeder      │
  │                              │                            │ GuthabenÀnderung                             │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ bankbalance                  │ double                     │ Beim Betreten eines Servers & bei jeder      │
  │                              │                            │ BankguthabenÀnderung                         │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ blockoftheday                │ UTF, UTF, int, UTF         │ Wenn der Block/die Entity des Tages          │
  │                              │                            │ aktualisiert wird                            │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ blockoftheday_progress       │ (keine)                    │ RegelmÀßig zur Synchronisierung des          │
  │                              │                            │ Fortschritts                                 │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ booster                      │ int, [UTF, int, ...]       │ Beim Betreten eines Servers & bei Änderung   │
  │                              │                            │ der aktiven Booster                          │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ clearlag                     │ long                       │ RegelmÀßig zur Anzeige der Zeit bis          │
  │                              │                            │ ClearLag                                     │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ entityremover                │ long                       │ RegelmÀßig zur Anzeige der Zeit bis          │
  │                              │                            │ Entity Remover                               │
  ├──────────────────────────────┌────────────────────────────┌───────────────────────────────────────────────
  │ plotchat_configuration       │ boolean                    │ Beim Betreten eines Servers & bei Änderung   │
  │                              │                            │ der PlotChat-Einstellung                     │
  └──────────────────────────────┮────────────────────────────┮──────────────────────────────────────────────┘