another cosmetics update
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"result": {
|
"result": {
|
||||||
"item": "szar:atom",
|
"item": "szar:nuke",
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user