diff --git a/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java b/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java index 02b5174..789462d 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java @@ -14,8 +14,10 @@ import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.PlayerContextManager; import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext; import ru.dragonestia.msb3.api.player.defaults.NavigatorContext; +import ru.dragonestia.msb3.api.player.defaults.TalksContext; import ru.dragonestia.msb3.api.resource.DialogueResources; import ru.dragonestia.msb3.api.resource.MonologueResources; +import ru.dragonestia.msb3.api.skin.SkinStorage; import ru.dragonestia.msb3.api.ui.BlackScreen; import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars; import ru.dragonestia.msb3.api.ui.navigator.Navigators; @@ -85,6 +87,8 @@ public final class ServerBootstrap { initializer.onResourcePackCompiled(Resources.getResourcePack()); initPlayerContextManager(); + + initDefaultSkins(); } private void initDefaultModules() { @@ -178,5 +182,10 @@ public final class ServerBootstrap { // Default contexts PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new); PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new); + PlayerContextManager.registerContext(TalksContext.class, TalksContext::new); + } + + private void initDefaultSkins() { + SkinStorage.loadSkin(SkinStorage.DEFAULT, ResourceFromJar.of("skins/default.msb3skin")); } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinData.java b/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinData.java new file mode 100644 index 0000000..3bced7a --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinData.java @@ -0,0 +1,45 @@ +package ru.dragonestia.msb3.api.skin; + +import com.google.gson.Gson; +import lombok.Getter; +import net.minestom.server.entity.PlayerSkin; +import team.unnamed.creative.base.Writable; + +public class SkinData { + + private final static Gson gson = new Gson(); + + @Getter private final String texture; + @Getter private final String signature; + private transient PlayerSkin skin; + + public SkinData(String texture, String signature) { + this.texture = texture; + this.signature = signature; + } + + public synchronized PlayerSkin getSkin() { + if (skin == null) { + skin = new PlayerSkin(texture, signature); + } + return skin; + } + + public String serialize() { + return gson.toJson(new Data(signature, texture)); + } + + public static SkinData fromSkin(PlayerSkin skin) { + return new SkinData(skin.textures(), skin.signature()); + } + + public static SkinData fromWriteable(Writable writable) { + try { + return gson.fromJson(writable.toUTF8String(), SkinData.class); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public record Data(String signature, String texture) {} +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinStorage.java b/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinStorage.java new file mode 100644 index 0000000..4a7886f --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/skin/SkinStorage.java @@ -0,0 +1,53 @@ +package ru.dragonestia.msb3.api.skin; + +import lombok.experimental.UtilityClass; +import lombok.extern.log4j.Log4j2; +import net.kyori.adventure.key.Key; +import net.minestom.server.entity.PlayerSkin; +import team.unnamed.creative.base.Writable; + +import java.io.File; +import java.io.FileWriter; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Log4j2 +@UtilityClass +public class SkinStorage { + + public final static Key DEFAULT = Key.key("msb3", "default"); + + private final Map skins = new ConcurrentHashMap<>(); + + public void saveSkinToFile(PlayerSkin skin, File file) { + try (var writer = new FileWriter(file)) { + writer.write(SkinData.fromSkin(skin).serialize()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + log.info("Saved skin to {}", file.getAbsolutePath()); + } + + public void loadSkin(Key key, Writable writable) { + var prev = skins.put(key, SkinData.fromWriteable(writable)); + + if (prev != null) { + log.warn("Detected duplicate skin key: {}", key.asString()); + } + } + + public boolean existsSkin(Key key) { + return skins.containsKey(key); + } + + public PlayerSkin getSkin(Key key) { + var skin = skins.get(key); + if (skin == null) { + log.warn("No skin for key '{}'. Using default", key.asString()); + + return skins.get(DEFAULT).getSkin(); + } + return skin.getSkin(); + } +} diff --git a/api/src/main/resources/skins/default.msb3skin b/api/src/main/resources/skins/default.msb3skin new file mode 100644 index 0000000..b898db8 --- /dev/null +++ b/api/src/main/resources/skins/default.msb3skin @@ -0,0 +1 @@ +{"signature":"nPVllmMmLrovNFJJDaQvbvukS0j1o0D98eYmEQ9jR514R4Xrk64Kc4+6CJQ/CNyGpkGdYQBGvH7Ki1HtKiF2s1wqKnQOKUiIHJFZr8ACkTnNWrwVMZZLFsjvZK9Q9t+xBcuTa9JCX9eumffwIxpJ6UYL496AZQUB9KqKEdgg9Iuz+fybsv+hVLwefvtGlg+OjHfT67NmFYqBt+jB/1JX29a1vS7nL/gqVn3BJvpb8Hj2ao6Lp/wqaLg9PRFEjvVUHWACSztQNGEAj1r3GX1BGzTdJ4xh3mEfoF0Q8KfzMMPKlJR3VG+ayDJnrMmZCqXLh0a4GrRC3Yu9IGS7U38JlDnCQIPj1Mj8K60yhr9qwz9QLRKdWeYOt62PiAG0i8Qo3qtS9chgjCBbgyQ2S73AsxSVgUBNRUvsAMf65ODfpKd90MIkjtNM3qdekeP90e6xQEYMWeyf7hAyoi5DFp0eF70WI8xU//VvlV4676MqJZ077Fhd9IbsSC/lFOCDbb6+5hEMDaJenFMlacPd4/HaGCgUNDaaVTl5wAHxjWRFvQQMQ4piobeHtRVtCLX02554HuCn7l6JmRj19Slel0Of4dACv/tMq1MzNzlaOT3tjmJQ5j6s1jPBWVc4cKvuHwhfod60JuvOtJK8kTbJn8IQvGE0pBVT8KJb/hd3qIVR2As\u003d","texture":"ewogICJ0aW1lc3RhbXAiIDogMTc0MTYxNTkyNTYyMSwKICAicHJvZmlsZUlkIiA6ICJiMTI4ZDVhOTA4MTE0ZDdmYmNjOTdlNGE4MDUwOTFlMiIsCiAgInByb2ZpbGVOYW1lIiA6ICJ0ZXJlbnRldiIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS80ZWY1ZDBiMTkwMjU2ODlhZWRiNDcwZThiYmM2MmZmZTM0ZDEwZjEyMjhmZTg0ZTU2MjNkYTFmYjcxN2RhODg0IgogICAgfQogIH0KfQ\u003d\u003d"} \ No newline at end of file