diff --git a/src/main/java/party/_2a03/mc/MinecraftTweaks2a03.java b/src/main/java/party/_2a03/mc/MinecraftTweaks2a03.java index 55872b3..2de7cc5 100644 --- a/src/main/java/party/_2a03/mc/MinecraftTweaks2a03.java +++ b/src/main/java/party/_2a03/mc/MinecraftTweaks2a03.java @@ -14,6 +14,8 @@ import party._2a03.mc.command.FlyCommand; import party._2a03.mc.command.HatCommand; import party._2a03.mc.command.HeadCommand; import party._2a03.mc.command.HomeCommand; +import party._2a03.mc.command.NameCommand; +import party._2a03.mc.command.SkinCommand; import party._2a03.mc.command.SpawnCommand; import party._2a03.mc.util.Config; import party._2a03.mc.util.Database; @@ -56,6 +58,8 @@ public class MinecraftTweaks2a03 implements ModInitializer { HatCommand.register(dispatcher); HeadCommand.register(dispatcher); HomeCommand.register(dispatcher); + NameCommand.register(dispatcher); + SkinCommand.register(dispatcher); SpawnCommand.register(dispatcher); } }); diff --git a/src/main/java/party/_2a03/mc/command/HomeCommand.java b/src/main/java/party/_2a03/mc/command/HomeCommand.java index 133e0b2..a23e631 100644 --- a/src/main/java/party/_2a03/mc/command/HomeCommand.java +++ b/src/main/java/party/_2a03/mc/command/HomeCommand.java @@ -12,6 +12,8 @@ import party._2a03.mc.util.Database; import party._2a03.mc.util.PlayerData; import party._2a03.mc.util.PlayerPosition; +import java.util.UUID; + public class HomeCommand { public static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder literalargumentbuilder = CommandManager.literal("home").executes(ctx -> { @@ -43,12 +45,13 @@ public class HomeCommand { return 1; })); - literalargumentbuilder.then(CommandManager.literal("sudoset").requires(ctx -> { + literalargumentbuilder.then(CommandManager.literal("assign").requires(ctx -> { return ctx.hasPermissionLevel(2); }).then(CommandManager.argument("UUID", StringArgumentType.word()).executes(ctx -> { + UUID uuid = UUID.fromString(StringArgumentType.getString(ctx, "UUID")); ServerCommandSource source = ctx.getSource(); ServerPlayerEntity sender = source.getPlayer(); - PlayerData playerdata = Database.getPlayer(StringArgumentType.getString(ctx, "UUID")); + PlayerData playerdata = Database.getPlayer(uuid); double x = sender.getX(); double y = sender.getY(); double z = sender.getZ(); @@ -57,7 +60,7 @@ public class HomeCommand { RegistryKey registrykey = sender.getWorld().getRegistryKey(); PlayerPosition location = new PlayerPosition(x, y, z, yaw, pitch, registrykey); playerdata.setHome(location); - source.sendFeedback(Text.of("User's home has been updated (" + StringArgumentType.getString(ctx, "UUID") + ")"), true); + source.sendFeedback(Text.of("User's home has been updated (" + uuid.toString() + ")"), true); return 1; }))); diff --git a/src/main/java/party/_2a03/mc/command/NameCommand.java b/src/main/java/party/_2a03/mc/command/NameCommand.java new file mode 100644 index 0000000..0e7613c --- /dev/null +++ b/src/main/java/party/_2a03/mc/command/NameCommand.java @@ -0,0 +1,32 @@ +package party._2a03.mc.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import party._2a03.mc.util.Database; +import party._2a03.mc.util.PlayerData; + +import java.util.UUID; + +public class NameCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(CommandManager.literal("name") + .then(CommandManager.literal("assign") + .requires(ctx -> { return ctx.hasPermissionLevel(2); }) + .then(CommandManager.argument("UUID", StringArgumentType.word()) + .then(CommandManager.argument("name", StringArgumentType.word()) + .executes(ctx -> { + UUID uuid = UUID.fromString(StringArgumentType.getString(ctx, "UUID")); + ServerCommandSource source = ctx.getSource(); + ServerPlayerEntity sender = source.getPlayer(); + PlayerData playerdata = Database.getPlayer(uuid); + playerdata.setName(StringArgumentType.getString(ctx, "name")); + source.sendFeedback(Text.of("User's name has been updated (" + uuid.toString() + ")"), true); + return 1; + }))))); + } +} diff --git a/src/main/java/party/_2a03/mc/command/SkinCommand.java b/src/main/java/party/_2a03/mc/command/SkinCommand.java new file mode 100644 index 0000000..6d2d047 --- /dev/null +++ b/src/main/java/party/_2a03/mc/command/SkinCommand.java @@ -0,0 +1,52 @@ +package party._2a03.mc.command; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import party._2a03.mc.util.Database; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.net.URL; +import java.util.UUID; + +public class SkinCommand { + public static void register(CommandDispatcher dispatcher) { + LiteralArgumentBuilder literalargumentbuilder = CommandManager.literal("skin"); + + literalargumentbuilder.then(CommandManager.literal("assign") + .requires(ctx -> { return ctx.hasPermissionLevel(2); }) + .then(CommandManager.argument("UUID", StringArgumentType.word()) + .then(CommandManager.argument("url", StringArgumentType.greedyString()) + .executes(ctx -> { + ServerCommandSource source = ctx.getSource(); + String magic, skinval, skinsig; + try { + URL url = new URL(StringArgumentType.getString(ctx, "url")); + BufferedReader read = new BufferedReader(new InputStreamReader(url.openStream())); + magic = read.readLine(); + skinval = read.readLine(); + skinsig = read.readLine(); + read.close(); + } catch (IOException e) { + source.sendFeedback(Text.of("Failed to access skin"), false); + return 1; + } + if (!magic.equals("SKIN:1") || skinval == null || skinsig == null) { + source.sendFeedback(Text.of("Invalid skin"), false); + return 1; + } + UUID uuid = UUID.fromString(StringArgumentType.getString(ctx, "UUID")); + Database.getPlayer(uuid).setTextures(skinval, skinsig); + source.sendFeedback(Text.of("User's skin has been updated (" + uuid.toString() + ")"), true); + return 1; + })))); + + dispatcher.register(literalargumentbuilder); + } +} diff --git a/src/main/java/party/_2a03/mc/mixin/MixinGameProfile.java b/src/main/java/party/_2a03/mc/mixin/MixinGameProfile.java new file mode 100644 index 0000000..6cd6d15 --- /dev/null +++ b/src/main/java/party/_2a03/mc/mixin/MixinGameProfile.java @@ -0,0 +1,33 @@ +package party._2a03.mc.mixin; + +import com.mojang.authlib.GameProfile; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import party._2a03.mc.util.Database; +import party._2a03.mc.util.PlayerData; + +import java.util.UUID; + +@Mixin(value = GameProfile.class, remap = false) +public class MixinGameProfile { + int ready = 0; + String server_name = ""; + + @Inject(method = "getName", at = @At("RETURN"), cancellable = true) + public void replaceName(CallbackInfoReturnable ci) { + if (ready++ < 2) // wait until authenticated + return; + UUID uuid = ((GameProfile)(Object)this).getId(); + if (uuid == null) // wait until UUID is set + return; + if (this.server_name == "") { + PlayerData playerdata = Database.getPlayer(uuid.toString()); + this.server_name = playerdata.getName(); + } + ci.setReturnValue(this.server_name); + } +} diff --git a/src/main/java/party/_2a03/mc/mixin/MixinPlayerManager.java b/src/main/java/party/_2a03/mc/mixin/MixinPlayerManager.java index 521102f..62c1d4d 100644 --- a/src/main/java/party/_2a03/mc/mixin/MixinPlayerManager.java +++ b/src/main/java/party/_2a03/mc/mixin/MixinPlayerManager.java @@ -1,5 +1,6 @@ package party._2a03.mc.mixin; +import com.mojang.authlib.properties.PropertyMap; import net.minecraft.entity.player.PlayerAbilities; import net.minecraft.server.PlayerManager; import net.minecraft.network.ClientConnection; @@ -11,9 +12,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import party._2a03.mc.util.Config; +import party._2a03.mc.util.Database; +import party._2a03.mc.util.PlayerData; @Mixin(PlayerManager.class) public abstract class MixinPlayerManager { + @Inject(method = "onPlayerConnect", at = @At(value = "HEAD")) + void onConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { + PlayerData playerdata = Database.getPlayer(player.getGameProfile().getId()); + PropertyMap map = player.getGameProfile().getProperties(); + map.removeAll("textures"); + map.put("textures", playerdata.getTextures()); + } + @Inject(method = "onPlayerConnect", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;getAbilities()Lnet/minecraft/entity/player/PlayerAbilities;")) public void persistFlight(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { diff --git a/src/main/java/party/_2a03/mc/mixin/MixinServerPlayerEntity.java b/src/main/java/party/_2a03/mc/mixin/MixinServerPlayerEntity.java index eeeda9b..3267163 100644 --- a/src/main/java/party/_2a03/mc/mixin/MixinServerPlayerEntity.java +++ b/src/main/java/party/_2a03/mc/mixin/MixinServerPlayerEntity.java @@ -3,10 +3,12 @@ package party._2a03.mc.mixin; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.registry.RegistryKey; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; import net.minecraft.world.World; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import party._2a03.mc.util.Config; diff --git a/src/main/java/party/_2a03/mc/util/Database.java b/src/main/java/party/_2a03/mc/util/Database.java index 5a9989b..72390d2 100644 --- a/src/main/java/party/_2a03/mc/util/Database.java +++ b/src/main/java/party/_2a03/mc/util/Database.java @@ -13,6 +13,8 @@ import org.json.JSONArray; import party._2a03.mc.util.PlayerData; import party._2a03.mc.util.PlayerPosition; +import java.util.UUID; + public class Database { private static final Logger LOGGER = LogManager.getLogger(); private static Connection conn = null; @@ -32,9 +34,12 @@ public class Database { stmt = conn.createStatement(); stmt.executeUpdate( "CREATE TABLE IF NOT EXISTS players (" + - "uuid TEXT NOT NULL UNIQUE," + - "home TEXT," + - "asn INTEGER," + + "uuid TEXT NOT NULL UNIQUE," + + "name TEXT," + + "skinval TEXT," + + "skinsig TEXT," + + "home TEXT," + + "asn INTEGER," + "PRIMARY KEY(uuid)" + ");"); stmt.executeUpdate( @@ -72,16 +77,26 @@ public class Database { } } + public static PlayerData getPlayer(UUID uuid) { + return getPlayer(uuid.toString()); + } + public static PlayerData getPlayer(String uuid) { try { PreparedStatement pstmt; pstmt = conn.prepareStatement( - "SELECT home FROM players WHERE uuid = ?;"); + "SELECT home, name, skinval, skinsig FROM players WHERE uuid = ?;"); pstmt.setString(1, uuid); ResultSet res = pstmt.executeQuery(); if (res.next()) { - PlayerPosition home = new PlayerPosition(new JSONArray(res.getString("home"))); - return new PlayerData(uuid, home); + String homeData = res.getString("home"); + PlayerPosition home = new PlayerPosition(); + if (homeData != "" && homeData != null) + home = new PlayerPosition(new JSONArray(homeData)); + String name = res.getString("name"); + String skinval = res.getString("skinval"); + String skinsig = res.getString("skinsig"); + return new PlayerData(uuid, home, name, skinval, skinsig); } pstmt.close(); } catch (SQLException e) { @@ -90,6 +105,44 @@ public class Database { return new PlayerData(uuid); } + public static void setPlayerTextures(String uuid, String skinval, String skinsig) { + try { + PreparedStatement pstmt; + pstmt = conn.prepareStatement( + "INSERT INTO players (uuid, skinval, skinsig) " + + "VALUES(?, ?, ?) " + + "ON CONFLICT(uuid) " + + "DO UPDATE SET skinval=?, skinsig=?;"); + pstmt.setString(1, uuid); + pstmt.setString(2, skinval); + pstmt.setString(3, skinsig); + pstmt.setString(4, skinval); + pstmt.setString(5, skinsig); + pstmt.executeUpdate(); + pstmt.close(); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + } + } + + public static void setPlayerName(String uuid, String name) { + try { + PreparedStatement pstmt; + pstmt = conn.prepareStatement( + "INSERT INTO players (uuid, name) " + + "VALUES(?, ?) " + + "ON CONFLICT(uuid) " + + "DO UPDATE SET name=?;"); + pstmt.setString(1, uuid); + pstmt.setString(2, name); + pstmt.setString(3, name); + pstmt.executeUpdate(); + pstmt.close(); + } catch (SQLException e) { + LOGGER.error(e.getMessage(), e); + } + } + public static void setPlayerHome(String uuid, PlayerPosition home) { try { PreparedStatement pstmt; diff --git a/src/main/java/party/_2a03/mc/util/PlayerData.java b/src/main/java/party/_2a03/mc/util/PlayerData.java index 73204f0..94126fc 100644 --- a/src/main/java/party/_2a03/mc/util/PlayerData.java +++ b/src/main/java/party/_2a03/mc/util/PlayerData.java @@ -1,5 +1,6 @@ package party._2a03.mc.util; +import com.mojang.authlib.properties.Property; import org.json.JSONArray; import org.json.JSONObject; import party._2a03.mc.util.Database; @@ -8,15 +9,33 @@ import party._2a03.mc.util.PlayerPosition; public class PlayerData { private String uuid; private PlayerPosition home; + private String name; + private String skinval; + private String skinsig; public PlayerData(String p_uuid) { this.uuid = p_uuid; this.home = new PlayerPosition(); } - public PlayerData(String p_uuid, PlayerPosition p_home) { + public PlayerData(String p_uuid, PlayerPosition p_home, String p_name, String p_skinval, String p_skinsig) { this.uuid = p_uuid; this.home = p_home; + this.name = p_name; + this.skinval = p_skinval; + this.skinsig = p_skinsig; + } + + public Property getTextures() { + // Return a Steve texture if unset + if (this.skinval == null || this.skinsig == null || this.skinval.length() == 0 || this.skinsig.length() == 0) + return new Property("textures", "ewogICJ0aW1lc3RhbXAiIDogMTY2NjMwMjkxNzkzOCwKICAicHJvZmlsZUlkIiA6ICJjOWRlZTM4MDUzYjg0YzI5YjZlZjA5YjJlMDM5OTc0ZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJTQVJfRGVjZW1iZXI1IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzMxZjQ3N2ViMWE3YmVlZTYzMWMyY2E2NGQwNmY4ZjY4ZmE5M2EzMzg2ZDA0NDUyYWIyN2Y0M2FjZGYxYjYwY2IiCiAgICB9CiAgfQp9", "tTCtASRIyuzlNUgSoXgUr6arxABhCR4EQ9+eHaUoO8bADljmUFoQfb6oba8zqe2gIa2mnu5KQaOPQCxcTDjgNv9aIL2smINKxy/60VE4Mgnrh5ntH+mGuDi00V3Bk2CsObFZXz1vgk2UxdQUQ41eVQYm2xBrXFEbXMSoTafWGv0FMTPFpGxGRdduTe3QTEie3GcfAMHCn/9xMMmUxZZ6UVZ+mDe8ARt9/cmK+GmqT8m3kmrz/vq+i29KV4tWvJqsKIVAXm97jVPH9XxVR3tYlheimQSFNrCU8SzNPum/ZhxNAf5Uw90+/K0eaJE59y8tS7KDV5DHrRrHHXb/ywGGklSri1YjFm9AEBk6BeH8Y3Ot/e+zfQbF3rOny2DkBAm/v28FooYd25gXB4MjUFNPj3KdveQh7DpRAvnkmBZMqJCO+Z9fdY4Dw+jmqjII88r6mukWAODvXed/x8bvv55zzNOAxtqtwBTWHIdqWFr/7pMZF26RY1Tluw+pAWGWaKMHtqlGzyOLGMxMKwXqtLNEpIYw52ETwGKaWh8h34cOoI8dhpjfjym4UOihMmazK9LC0EUEHuBlgy5b/Ae71+6UsLNIX8bJwIvN16sP6wpSTNbV6htWoS7/ehvoxdKhI6XEUqWgEoAwmquClPfWiveCV057reoKeVHB9RdTl0sW+HM="); + // Otherwise... + return new Property("textures", this.skinval, this.skinsig); + } + + public String getName() { + return (this.name == null || this.name.length() == 0) ? "Unknown" : this.name; } public PlayerPosition getHome() { @@ -27,6 +46,17 @@ public class PlayerData { return this.uuid; } + public void setTextures(String skinval, String skinsig) { + this.skinval = skinval; + this.skinsig = skinsig; + Database.setPlayerTextures(this.uuid, this.skinval, this.skinsig); + } + + public void setName(String name) { + this.name = name; + Database.setPlayerName(this.uuid, this.name); + } + public void setHome(PlayerPosition location) { this.home = location; Database.setPlayerHome(this.uuid, this.home); @@ -36,6 +66,7 @@ public class PlayerData { JSONObject json = new JSONObject(); json.put("uuid", uuid); json.put("home", home.getJSON()); + json.put("name", name); return json; } } diff --git a/src/main/resources/minecraft-tweaks-2a03.mixins.json b/src/main/resources/minecraft-tweaks-2a03.mixins.json index ff70cb0..a89278a 100644 --- a/src/main/resources/minecraft-tweaks-2a03.mixins.json +++ b/src/main/resources/minecraft-tweaks-2a03.mixins.json @@ -5,6 +5,7 @@ "server": [ "MixinCreeperEntity", "MixinGameModeCommand", + "MixinGameProfile", "MixinPlayerManager", "MixinRespawnAnchorBlock", "MixinServerPlayerEntity",