another cosmetics update

This commit is contained in:
2026-02-23 11:28:17 +01:00
parent 0f14f00bea
commit 8ff7e4e4ec
6 changed files with 136 additions and 45 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10 yarn_mappings=1.20.1+build.10
loader_version=0.18.3 loader_version=0.18.3
# Mod Properties # Mod Properties
mod_version=26.2.22.2 mod_version=26.2.23
maven_group=dev.tggamesyt maven_group=dev.tggamesyt
archives_base_name=szar archives_base_name=szar
# Dependencies # Dependencies

View File

@@ -1,8 +1,12 @@
package dev.tggamesyt.szar.client; package dev.tggamesyt.szar.client;
import com.google.gson.*; import com.google.gson.*;
import com.mojang.authlib.GameProfile;
import dev.tggamesyt.szar.ServerCosmetics.NameType;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
@@ -17,12 +21,10 @@ import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import static dev.tggamesyt.szar.ServerCosmetics.MOJANG_CAPES_SYNC;
public class ClientCosmetics { public class ClientCosmetics {
public enum NameType {
STATIC,
GRADIENT
}
public static class CosmeticProfile { public static class CosmeticProfile {
public NameType nameType; public NameType nameType;
@@ -34,9 +36,6 @@ public class ClientCosmetics {
private static final Map<UUID, CosmeticProfile> PROFILES = new HashMap<>(); private static final Map<UUID, CosmeticProfile> PROFILES = new HashMap<>();
// Player UUID -> Mojang cape list
public static final Map<UUID, List<MojangCape>> MOJANG_CAPES = new HashMap<>();
public static class MojangCape { public static class MojangCape {
public String id; public String id;
public String name; public String name;
@@ -106,8 +105,8 @@ public class ClientCosmetics {
/* ---------------- FETCH MOJANG CAPES ---------------- */ /* ---------------- FETCH MOJANG CAPES ---------------- */
public static void fetchMojangCapes(UUID uuid) { public static void fetchMojangCapes(UUID uuid) {
try { try {MinecraftClient client = MinecraftClient.getInstance();
String accessToken = getAccessTokenFromLaunchArgs(); String accessToken = client.getSession().getAccessToken();
if (accessToken == null) return; if (accessToken == null) return;
URL url = new URL("https://api.minecraftservices.com/minecraft/profile"); URL url = new URL("https://api.minecraftservices.com/minecraft/profile");
@@ -119,8 +118,6 @@ public class ClientCosmetics {
String json = new String(in.readAllBytes()); String json = new String(in.readAllBytes());
in.close(); in.close();
System.out.println("[ClientCosmetics] Mojang capes JSON: " + json);
JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
JsonArray capes = obj.getAsJsonArray("capes"); JsonArray capes = obj.getAsJsonArray("capes");
if (capes == null) return; if (capes == null) return;
@@ -135,7 +132,17 @@ public class ClientCosmetics {
list.add(cape); list.add(cape);
} }
MOJANG_CAPES.put(uuid, list); PacketByteBuf buf = PacketByteBufs.create();
buf.writeUuid(uuid);
buf.writeInt(list.size());
for (MojangCape cape : list) {
buf.writeString(cape.id);
buf.writeString(cape.name);
buf.writeString(cape.url);
}
ClientPlayNetworking.send(MOJANG_CAPES_SYNC, buf);
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
@@ -143,13 +150,20 @@ public class ClientCosmetics {
} }
private static String getAccessTokenFromLaunchArgs() { private static String getAccessTokenFromLaunchArgs() {
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) { MinecraftClient client = MinecraftClient.getInstance();
if (arg.startsWith("--accessToken")) { return client.getSession().getAccessToken();
String[] split = arg.split("=", 2);
if (split.length == 2) return split[1];
else continue;
}
} }
public static Identifier loadTextureFromURL(String url, String playerId) {
if (url == null || url.isEmpty() || playerId == null || playerId.isEmpty()) return null;
try (InputStream in = new URL(url).openStream()) {
NativeImage img = NativeImage.read(in);
NativeImageBackedTexture texture = new NativeImageBackedTexture(img);
Identifier id = new Identifier("szar", "cape_" + playerId);
MinecraftClient.getInstance().getTextureManager().registerTexture(id, texture);
return id;
} catch (Exception e) {
e.printStackTrace();
return null; return null;
} }
} }
}

View File

@@ -5,6 +5,7 @@ import dev.tggamesyt.szar.NyanEntity;
import dev.tggamesyt.szar.PlaneEntity; import dev.tggamesyt.szar.PlaneEntity;
import dev.tggamesyt.szar.Szar; import dev.tggamesyt.szar.Szar;
import dev.tggamesyt.szar.PlaneAnimation; import dev.tggamesyt.szar.PlaneAnimation;
import dev.tggamesyt.szar.ServerCosmetics.NameType;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -43,7 +44,9 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.*; import java.util.*;
import static dev.tggamesyt.szar.ServerCosmetics.SYNC_PACKET;
import static dev.tggamesyt.szar.Szar.*; import static dev.tggamesyt.szar.Szar.*;
import static dev.tggamesyt.szar.client.ClientCosmetics.loadTextureFromURL;
import static dev.tggamesyt.szar.client.UraniumUtils.updateUranium; import static dev.tggamesyt.szar.client.UraniumUtils.updateUranium;
public class SzarClient implements ClientModInitializer { public class SzarClient implements ClientModInitializer {
@@ -73,6 +76,25 @@ public class SzarClient implements ClientModInitializer {
int loopStart = startOffset + startLength; int loopStart = startOffset + startLength;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ClientPlayNetworking.registerGlobalReceiver(SYNC_PACKET, (client, handler, buf, responseSender) -> {
// First read the player UUID
UUID playerUuid = buf.readUuid();
// Read cosmetic data
NameType nameType = buf.readEnumConstant(NameType.class);
Integer staticColor = buf.readBoolean() ? buf.readInt() : null;
Integer gradientStart = buf.readBoolean() ? buf.readInt() : null;
Integer gradientEnd = gradientStart != null ? buf.readInt() : null;
String textureUrl = buf.readString();
Identifier capeTexture = loadTextureFromURL(textureUrl, playerUuid.toString());
// Apply the cosmetic profile on the main thread
client.execute(() -> {
ClientCosmetics.fetchMojangCapes(playerUuid);
ClientCosmetics.apply(playerUuid, nameType, staticColor, gradientStart, gradientEnd, capeTexture);
});
});
ClientPlayNetworking.registerGlobalReceiver(Szar.OPEN_MERL_SCREEN, ClientPlayNetworking.registerGlobalReceiver(Szar.OPEN_MERL_SCREEN,
(client, handler, buf, responseSender) -> { (client, handler, buf, responseSender) -> {
int entityId = buf.readInt(); int entityId = buf.readInt();

View File

@@ -2,8 +2,8 @@ package dev.tggamesyt.szar;
import com.google.gson.*; import com.google.gson.*;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import dev.tggamesyt.szar.client.ClientCosmetics.NameType;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
@@ -20,6 +20,7 @@ public class ServerCosmetics {
public static final Identifier SYNC_PACKET = public static final Identifier SYNC_PACKET =
new Identifier(Szar.MOD_ID, "cosmetic_sync"); new Identifier(Szar.MOD_ID, "cosmetic_sync");
public static final Identifier MOJANG_CAPES_SYNC = new Identifier(Szar.MOD_ID, "mojang_capes_sync");
private static final String CAPES_URL = private static final String CAPES_URL =
"https://raw.githubusercontent.com/tggamesyt/szar/main/capes.json"; "https://raw.githubusercontent.com/tggamesyt/szar/main/capes.json";
@@ -51,6 +52,40 @@ public class ServerCosmetics {
public static void init() { public static void init() {
loadJson(); loadJson();
registerCommand(); registerCommand();
ServerPlayNetworking.registerGlobalReceiver(MOJANG_CAPES_SYNC, (server, player, handler, buf, responseSender) -> {
UUID uuid = buf.readUuid();
int size = buf.readInt();
List<MojangCape> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
MojangCape c = new MojangCape();
c.id = buf.readString();
c.name = buf.readString();
c.url = buf.readString();
list.add(c);
}
PLAYER_MOJANG_CAPES.put(uuid, list);
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
ServerPlayerEntity player = handler.getPlayer();
// Send this player's own cosmetics to themselves
UserCosmetics user = USERS.get(player.getUuid());
if (user != null) {
sync(player, user);
}
// Optionally: send all other players' cosmetics to this new player
for (ServerPlayerEntity other : server.getPlayerManager().getPlayerList()) {
if (other.equals(player)) continue;
UserCosmetics otherUser = USERS.get(other.getUuid());
if (otherUser != null) {
sync(player, otherUser); // send other players cosmetics to the new player
}
}
});
} }
/* ---------------- LOAD JSON ---------------- */ /* ---------------- LOAD JSON ---------------- */
@@ -117,7 +152,7 @@ public class ServerCosmetics {
private static void registerCommand() { private static void registerCommand() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) ->
dispatcher.register(CommandManager.literal("cape") dispatcher.register(CommandManager.literal("cape")
.then(CommandManager.argument("id", StringArgumentType.word()) .then(CommandManager.argument("id", StringArgumentType.greedyString()) // <-- change here
.suggests((ctx, builder) -> { .suggests((ctx, builder) -> {
ServerPlayerEntity player = ctx.getSource().getPlayer(); ServerPlayerEntity player = ctx.getSource().getPlayer();
@@ -129,16 +164,16 @@ public class ServerCosmetics {
// Mojang capes // Mojang capes
List<MojangCape> mojang = PLAYER_MOJANG_CAPES.get(player.getUuid()); List<MojangCape> mojang = PLAYER_MOJANG_CAPES.get(player.getUuid());
if (mojang != null) { if (mojang != null) {
for (MojangCape c : mojang) builder.suggest(c.name); for (MojangCape c : mojang)
builder.suggest(c.name);
} }
builder.suggest("none"); builder.suggest("none");
return builder.buildFuture(); return builder.buildFuture();
}) })
.executes(ctx -> { .executes(ctx -> {
ServerPlayerEntity player = ctx.getSource().getPlayer(); ServerPlayerEntity player = ctx.getSource().getPlayer();
String id = StringArgumentType.getString(ctx, "id"); String id = StringArgumentType.getString(ctx, "id"); // this now includes spaces
UserCosmetics user = USERS.get(player.getUuid()); UserCosmetics user = USERS.get(player.getUuid());
if (user == null) return 0; if (user == null) return 0;
@@ -156,7 +191,7 @@ public class ServerCosmetics {
if (mojang != null) { if (mojang != null) {
for (MojangCape c : mojang) { for (MojangCape c : mojang) {
if (c.name.equalsIgnoreCase(id)) { if (c.name.equalsIgnoreCase(id)) {
user.selectedCape = null; // vanilla cape user.selectedCape = c.id; // vanilla cape
sync(player, user); sync(player, user);
player.sendMessage(Text.literal("Equipped Mojang cape: " + c.name), false); player.sendMessage(Text.literal("Equipped Mojang cape: " + c.name), false);
return 1; return 1;
@@ -176,15 +211,19 @@ public class ServerCosmetics {
return 1; return 1;
}) })
) )
) ));
);
} }
/* ---------------- SYNC ---------------- */ /* ---------------- SYNC ---------------- */
public static void sync(ServerPlayerEntity player, UserCosmetics user) { public static void sync(ServerPlayerEntity player, UserCosmetics user) {
for (ServerPlayerEntity p : player.getServer().getPlayerManager().getPlayerList()) {
PacketByteBuf buf = PacketByteBufs.create(); PacketByteBuf buf = PacketByteBufs.create();
// Write player UUID first
buf.writeUuid(player.getUuid());
// Cosmetic data
buf.writeEnumConstant(user.nameType); buf.writeEnumConstant(user.nameType);
buf.writeBoolean(user.staticColor != null); buf.writeBoolean(user.staticColor != null);
if (user.staticColor != null) buf.writeInt(user.staticColor); if (user.staticColor != null) buf.writeInt(user.staticColor);
@@ -195,11 +234,27 @@ public class ServerCosmetics {
buf.writeInt(user.gradientEnd); buf.writeInt(user.gradientEnd);
} }
String textureUrl = user.selectedCape != null String textureUrl = null;
? CAPES.get(user.selectedCape) if (user.selectedCape != null) {
: ""; textureUrl = CAPES.get(user.selectedCape);
if (textureUrl == null) {
List<MojangCape> mojang = PLAYER_MOJANG_CAPES.get(player.getUuid());
if (mojang != null) {
for (MojangCape c : mojang) {
if (c.id.equalsIgnoreCase(user.selectedCape)) {
textureUrl = c.url;
break;
}
}
}
}
}
buf.writeString(textureUrl == null ? "" : textureUrl); buf.writeString(textureUrl == null ? "" : textureUrl);
ServerPlayNetworking.send(player, SYNC_PACKET, buf); ServerPlayNetworking.send(p, SYNC_PACKET, buf);
}
}
public enum NameType {
STATIC,
GRADIENT
} }
} }

View File

@@ -14,7 +14,7 @@
} }
}, },
"result": { "result": {
"item": "szar:atom", "item": "szar:nuke",
"count": 1 "count": 1
} }
} }

View File

@@ -10,7 +10,7 @@
"item": "minecraft:redstone" "item": "minecraft:redstone"
}, },
"A": { "A": {
"item": "szar:atom" "item": "szar:nuke"
}, },
"I": { "I": {
"item": "minecraft:iron_ingot" "item": "minecraft:iron_ingot"