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
loader_version=0.18.3
# Mod Properties
mod_version=26.2.22.2
mod_version=26.2.23
maven_group=dev.tggamesyt
archives_base_name=szar
# Dependencies

View File

@@ -1,8 +1,12 @@
package dev.tggamesyt.szar.client;
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.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.network.PacketByteBuf;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.text.MutableText;
@@ -17,12 +21,10 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import static dev.tggamesyt.szar.ServerCosmetics.MOJANG_CAPES_SYNC;
public class ClientCosmetics {
public enum NameType {
STATIC,
GRADIENT
}
public static class CosmeticProfile {
public NameType nameType;
@@ -34,9 +36,6 @@ public class ClientCosmetics {
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 String id;
public String name;
@@ -106,8 +105,8 @@ public class ClientCosmetics {
/* ---------------- FETCH MOJANG CAPES ---------------- */
public static void fetchMojangCapes(UUID uuid) {
try {
String accessToken = getAccessTokenFromLaunchArgs();
try {MinecraftClient client = MinecraftClient.getInstance();
String accessToken = client.getSession().getAccessToken();
if (accessToken == null) return;
URL url = new URL("https://api.minecraftservices.com/minecraft/profile");
@@ -119,8 +118,6 @@ public class ClientCosmetics {
String json = new String(in.readAllBytes());
in.close();
System.out.println("[ClientCosmetics] Mojang capes JSON: " + json);
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
JsonArray capes = obj.getAsJsonArray("capes");
if (capes == null) return;
@@ -135,7 +132,17 @@ public class ClientCosmetics {
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) {
ex.printStackTrace();
@@ -143,13 +150,20 @@ public class ClientCosmetics {
}
private static String getAccessTokenFromLaunchArgs() {
for (String arg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
if (arg.startsWith("--accessToken")) {
String[] split = arg.split("=", 2);
if (split.length == 2) return split[1];
else continue;
}
MinecraftClient client = MinecraftClient.getInstance();
return client.getSession().getAccessToken();
}
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;
}
}
}

View File

@@ -5,6 +5,7 @@ import dev.tggamesyt.szar.NyanEntity;
import dev.tggamesyt.szar.PlaneEntity;
import dev.tggamesyt.szar.Szar;
import dev.tggamesyt.szar.PlaneAnimation;
import dev.tggamesyt.szar.ServerCosmetics.NameType;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
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.util.*;
import static dev.tggamesyt.szar.ServerCosmetics.SYNC_PACKET;
import static dev.tggamesyt.szar.Szar.*;
import static dev.tggamesyt.szar.client.ClientCosmetics.loadTextureFromURL;
import static dev.tggamesyt.szar.client.UraniumUtils.updateUranium;
public class SzarClient implements ClientModInitializer {
@@ -73,6 +76,25 @@ public class SzarClient implements ClientModInitializer {
int loopStart = startOffset + startLength;
@Override
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,
(client, handler, buf, responseSender) -> {
int entityId = buf.readInt();

View File

@@ -2,8 +2,8 @@ package dev.tggamesyt.szar;
import com.google.gson.*;
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.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -20,6 +20,7 @@ public class ServerCosmetics {
public static final Identifier SYNC_PACKET =
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 =
"https://raw.githubusercontent.com/tggamesyt/szar/main/capes.json";
@@ -51,6 +52,40 @@ public class ServerCosmetics {
public static void init() {
loadJson();
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 ---------------- */
@@ -117,7 +152,7 @@ public class ServerCosmetics {
private static void registerCommand() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) ->
dispatcher.register(CommandManager.literal("cape")
.then(CommandManager.argument("id", StringArgumentType.word())
.then(CommandManager.argument("id", StringArgumentType.greedyString()) // <-- change here
.suggests((ctx, builder) -> {
ServerPlayerEntity player = ctx.getSource().getPlayer();
@@ -129,16 +164,16 @@ public class ServerCosmetics {
// Mojang capes
List<MojangCape> mojang = PLAYER_MOJANG_CAPES.get(player.getUuid());
if (mojang != null) {
for (MojangCape c : mojang) builder.suggest(c.name);
for (MojangCape c : mojang)
builder.suggest(c.name);
}
builder.suggest("none");
return builder.buildFuture();
})
.executes(ctx -> {
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());
if (user == null) return 0;
@@ -156,7 +191,7 @@ public class ServerCosmetics {
if (mojang != null) {
for (MojangCape c : mojang) {
if (c.name.equalsIgnoreCase(id)) {
user.selectedCape = null; // vanilla cape
user.selectedCape = c.id; // vanilla cape
sync(player, user);
player.sendMessage(Text.literal("Equipped Mojang cape: " + c.name), false);
return 1;
@@ -176,15 +211,19 @@ public class ServerCosmetics {
return 1;
})
)
)
);
));
}
/* ---------------- SYNC ---------------- */
public static void sync(ServerPlayerEntity player, UserCosmetics user) {
for (ServerPlayerEntity p : player.getServer().getPlayerManager().getPlayerList()) {
PacketByteBuf buf = PacketByteBufs.create();
// Write player UUID first
buf.writeUuid(player.getUuid());
// Cosmetic data
buf.writeEnumConstant(user.nameType);
buf.writeBoolean(user.staticColor != null);
if (user.staticColor != null) buf.writeInt(user.staticColor);
@@ -195,11 +234,27 @@ public class ServerCosmetics {
buf.writeInt(user.gradientEnd);
}
String textureUrl = user.selectedCape != null
? CAPES.get(user.selectedCape)
: "";
String textureUrl = null;
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);
ServerPlayNetworking.send(player, SYNC_PACKET, buf);
ServerPlayNetworking.send(p, SYNC_PACKET, buf);
}
}
public enum NameType {
STATIC,
GRADIENT
}
}

View File

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

View File

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