[Feature] Update Checker
This commit is contained in:
parent
2ef9e51ef1
commit
69d472d107
21 changed files with 633 additions and 22 deletions
|
@ -17,6 +17,7 @@ import org.betterx.bclib.api.v3.levelgen.features.blockpredicates.BlockPredicate
|
|||
import org.betterx.bclib.api.v3.levelgen.features.placement.PlacementModifiers;
|
||||
import org.betterx.bclib.commands.CommandRegistry;
|
||||
import org.betterx.bclib.config.Configs;
|
||||
import org.betterx.bclib.networking.VersionChecker;
|
||||
import org.betterx.bclib.recipes.AnvilRecipe;
|
||||
import org.betterx.bclib.recipes.CraftingRecipes;
|
||||
import org.betterx.bclib.registry.BaseBlockEntities;
|
||||
|
@ -79,7 +80,8 @@ public class BCLib implements ModInitializer {
|
|||
Configs.save();
|
||||
|
||||
WorldsTogether.FORCE_SERVER_TO_BETTERX_PRESET = Configs.SERVER_CONFIG.forceBetterXPreset();
|
||||
|
||||
VersionChecker.registerMod(MOD_ID);
|
||||
|
||||
if (false && isDevEnvironment()) {
|
||||
BCLBiome theYellow = BCLBiomeBuilder
|
||||
.start(makeID("the_yellow"))
|
||||
|
@ -150,7 +152,6 @@ public class BCLib implements ModInitializer {
|
|||
.surface(Blocks.PURPLE_CONCRETE)
|
||||
.build();
|
||||
BiomeAPI.registerNetherBiome(thePurple);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.betterx.bclib.interfaces.PostInitable;
|
|||
import org.betterx.bclib.interfaces.RenderLayerProvider;
|
||||
import org.betterx.bclib.interfaces.TagProvider;
|
||||
import org.betterx.bclib.interfaces.tools.*;
|
||||
import org.betterx.bclib.networking.VersionChecker;
|
||||
import org.betterx.bclib.registry.BaseBlockEntities;
|
||||
import org.betterx.worlds.together.tag.v3.MineableTags;
|
||||
import org.betterx.worlds.together.tag.v3.TagManager;
|
||||
|
@ -73,6 +74,8 @@ public class PostInitAPI {
|
|||
itemTags = null;
|
||||
InternalBiomeAPI.loadFabricAPIBiomes();
|
||||
Configs.BIOMES_CONFIG.saveChanges();
|
||||
|
||||
VersionChecker.startCheck(isClient);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package org.betterx.bclib.client.gui.screens;
|
||||
|
||||
import org.betterx.bclib.config.Configs;
|
||||
import org.betterx.bclib.networking.VersionChecker;
|
||||
import org.betterx.ui.ColorUtil;
|
||||
import org.betterx.ui.layout.components.HorizontalStack;
|
||||
import org.betterx.ui.layout.components.LayoutComponent;
|
||||
import org.betterx.ui.layout.components.VerticalStack;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class UpdatesScreen extends BCLibLayoutScreen {
|
||||
|
||||
public static final String DONATION_URL = "https://www.paypal.com/donate/?hosted_button_id=7VTXYRXBHZQZJ&item_name=BetterX%20Mods&cmd=_s-xclick";
|
||||
|
||||
public UpdatesScreen(Screen parent) {
|
||||
super(parent, Component.translatable("bclib.updates.title"), 10, 10, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutComponent<?, ?> initContent() {
|
||||
VerticalStack rows = new VerticalStack(relative(1), fit()).centerHorizontal();
|
||||
rows.addMultilineText(fill(), fit(), Component.translatable("bclib.updates.description"))
|
||||
.centerHorizontal();
|
||||
|
||||
rows.addSpacer(16);
|
||||
|
||||
VersionChecker.forEachUpdate((mod, cur, updated) -> {
|
||||
ModContainer nfo = FabricLoader.getInstance().getModContainer(mod).orElse(null);
|
||||
|
||||
HorizontalStack row = rows.addRow(relative(0.8), fit()).centerHorizontal();
|
||||
if (nfo != null) {
|
||||
row.addText(fit(), fit(), Component.literal(nfo.getMetadata().getName()))
|
||||
.setColor(ColorUtil.WHITE);
|
||||
} else {
|
||||
row.addText(fit(), fit(), Component.literal(mod)).setColor(ColorUtil.WHITE);
|
||||
}
|
||||
row.addSpacer(4);
|
||||
row.addText(fit(), fit(), Component.literal(cur));
|
||||
row.addText(fit(), fit(), Component.literal(" -> "));
|
||||
row.addText(fit(), fit(), Component.literal(updated)).setColor(ColorUtil.GREEN);
|
||||
row.addFiller();
|
||||
if (nfo != null && nfo.getMetadata().getContact().get("homepage").isPresent()) {
|
||||
row.addButton(fit(), fit(), Component.translatable("bclib.updates.curseforge_link"))
|
||||
.onPress((bt) -> {
|
||||
this.openLink(nfo.getMetadata().getContact().get("homepage").get());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
VerticalStack layout = new VerticalStack(relative(1), fill()).centerHorizontal();
|
||||
layout.addSpacer(8);
|
||||
layout.addScrollable(rows);
|
||||
layout.addSpacer(8);
|
||||
|
||||
|
||||
HorizontalStack footer = layout.addRow(fill(), fit());
|
||||
if (Configs.CLIENT_CONFIG.isDonor()) {
|
||||
footer.addButton(
|
||||
fit(),
|
||||
fit(),
|
||||
Component.translatable("bclib.updates.donate").setStyle(Style.EMPTY.withColor(ColorUtil.YELLOW))
|
||||
)
|
||||
.onPress((bt) -> openLink(DONATION_URL));
|
||||
footer.addSpacer(2);
|
||||
footer.addMultilineText(fit(), fit(), Component.translatable("bclib.updates.donate_pre"))
|
||||
.alignBottom();
|
||||
}
|
||||
|
||||
footer.addFiller();
|
||||
footer.addCheckbox(
|
||||
fit(),
|
||||
fit(),
|
||||
Component.translatable("Disable Check"),
|
||||
!Configs.MAIN_CONFIG.checkVersions()
|
||||
)
|
||||
.onChange((cb, state) -> {
|
||||
Configs.MAIN_CONFIG.setCheckVersions(!state);
|
||||
Configs.MAIN_CONFIG.saveChanges();
|
||||
});
|
||||
footer.addSpacer(4);
|
||||
footer.addButton(fit(), fit(), CommonComponents.GUI_DONE).onPress((bt -> {
|
||||
onClose();
|
||||
}));
|
||||
return layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBackground(PoseStack poseStack, int i, int j, float f) {
|
||||
GuiComponent.fill(poseStack, 0, 0, width, height, 0xBD343444);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package org.betterx.bclib.client.gui.screens;
|
||||
|
||||
import org.betterx.bclib.config.Configs;
|
||||
import org.betterx.ui.ColorUtil;
|
||||
import org.betterx.ui.layout.components.*;
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
||||
public class WelcomeScreen extends BCLibLayoutScreen {
|
||||
public WelcomeScreen(Screen parent) {
|
||||
super(parent, translatable("bclib.welcome.title"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LayoutComponent<?, ?> initContent() {
|
||||
VerticalStack content = new VerticalStack(fill(), fit()).setDebugName("content");
|
||||
content.addMultilineText(fill(), fit(), MultiLineText.parse(translatable("bclib.welcome.description")))
|
||||
.centerHorizontal();
|
||||
if (Configs.CLIENT_CONFIG.isDonor()) {
|
||||
content.addHorizontalSeparator(48);
|
||||
HorizontalStack donationRow = content.addRow(relative(0.9), fit())
|
||||
.setDebugName("donationRow")
|
||||
.centerHorizontal();
|
||||
|
||||
donationRow.addMultilineText(fill(), fit(), translatable("bclib.welcome.donation"))
|
||||
.alignLeft()
|
||||
.alignRight();
|
||||
donationRow.addSpacer(4);
|
||||
donationRow.addButton(
|
||||
fit(), fit(),
|
||||
Component.translatable("bclib.updates.donate").setStyle(Style.EMPTY.withColor(ColorUtil.YELLOW))
|
||||
)
|
||||
.onPress((bt) -> openLink(UpdatesScreen.DONATION_URL)).centerVertical();
|
||||
}
|
||||
|
||||
content.addHorizontalSeparator(48);
|
||||
content.addCheckbox(fit(), fit(), translatable("bclib.welcome.updater.title"), true)
|
||||
.onChange((cb, state) -> Configs.MAIN_CONFIG.setCheckVersions(state));
|
||||
content.addSpacer(2);
|
||||
content.indent(24)
|
||||
.addMultilineText(fill(), fit(), translatable("bclib.welcome.updater.description"))
|
||||
.setColor(ColorUtil.GRAY);
|
||||
|
||||
content.addSpacer(16);
|
||||
content.addButton(fit(), fit(), CommonComponents.GUI_PROCEED).onPress((bt) -> {
|
||||
Configs.MAIN_CONFIG.setDidShowWelcomeScreen();
|
||||
onClose();
|
||||
}).alignRight();
|
||||
|
||||
return VerticalScroll.create(fill(), fill(), content);
|
||||
}
|
||||
}
|
|
@ -53,7 +53,10 @@ public class CommandRegistry {
|
|||
.then(Commands.literal("dimensions")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
.executes(ctx -> PrintInfo.printDimensions(ctx))
|
||||
)
|
||||
).then(Commands.literal("updates")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
.executes(ctx -> PrintInfo.printUpdates(ctx, true))
|
||||
)
|
||||
)
|
||||
.then(Commands.literal("debug_ore")
|
||||
.requires(source -> source.hasPermission(Commands.LEVEL_OWNERS))
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
package org.betterx.bclib.commands;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.client.gui.screens.UpdatesScreen;
|
||||
import org.betterx.bclib.config.Configs;
|
||||
import org.betterx.bclib.networking.VersionChecker;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
|
||||
public class PrintInfo {
|
||||
static int printDimensions(CommandContext<CommandSourceStack> ctx) {
|
||||
|
||||
|
@ -37,4 +47,70 @@ public class PrintInfo {
|
|||
ctx.getSource().sendSuccess(result, false);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
static int printUpdates(CommandContext<CommandSourceStack> ctx, boolean withUI) {
|
||||
boolean hasOne = false;
|
||||
MutableComponent header = Component.literal("Mod Updates:")
|
||||
.setStyle(Style.EMPTY.withBold(false)
|
||||
.withColor(ChatFormatting.WHITE));
|
||||
ctx.getSource().sendSuccess(header, false);
|
||||
|
||||
VersionChecker.forEachUpdate((mod, cur, updated) -> {
|
||||
ModContainer nfo = FabricLoader.getInstance().getModContainer(mod).orElse(null);
|
||||
MutableComponent result = Component.literal(" - ")
|
||||
.setStyle(Style.EMPTY.withBold(false)
|
||||
.withUnderlined(false)
|
||||
.withColor(ChatFormatting.WHITE));
|
||||
if (nfo != null)
|
||||
result.append(Component.literal(nfo.getMetadata().getName())
|
||||
.setStyle(Style.EMPTY.withBold(false).withColor(ChatFormatting.WHITE)));
|
||||
else
|
||||
result.append(Component.literal(mod)
|
||||
.setStyle(Style.EMPTY.withBold(false).withColor(ChatFormatting.WHITE)));
|
||||
result.append(Component.literal(": ")
|
||||
.setStyle(Style.EMPTY.withBold(false).withColor(ChatFormatting.WHITE)));
|
||||
result.append(Component.literal(cur).setStyle(Style.EMPTY.withBold(false).withColor(ChatFormatting.WHITE)));
|
||||
result.append(Component.literal(" -> ")
|
||||
.setStyle(Style.EMPTY.withBold(false).withColor(ChatFormatting.WHITE)));
|
||||
if (nfo != null && nfo.getMetadata().getContact().get("homepage").isPresent()) {
|
||||
var ce = new ClickEvent(
|
||||
ClickEvent.Action.OPEN_URL,
|
||||
nfo.getMetadata().getContact().get("homepage").get()
|
||||
);
|
||||
|
||||
result.append(Component.literal(updated)
|
||||
.setStyle(Style.EMPTY.withClickEvent(ce)
|
||||
.withBold(false)
|
||||
.withItalic(true)
|
||||
.withColor(ChatFormatting.GREEN)));
|
||||
result.append(Component.literal(" ")
|
||||
.setStyle(Style.EMPTY.withClickEvent(ce).withBold(true).withItalic(false)));
|
||||
|
||||
result = result.append(Component.literal("[CurseForge]")
|
||||
.setStyle(Style.EMPTY.withClickEvent(ce)
|
||||
.withBold(true)
|
||||
.withColor(ChatFormatting.GREEN)
|
||||
.withUnderlined(true)));
|
||||
|
||||
} else {
|
||||
result.append(Component.literal(updated)
|
||||
.setStyle(Style.EMPTY.withBold(false)
|
||||
.withItalic(true)
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
result.append(Component.literal(" ").setStyle(Style.EMPTY.withBold(true).withItalic(false)));
|
||||
|
||||
}
|
||||
ctx.getSource().sendSuccess(result, false);
|
||||
});
|
||||
MutableComponent footer = Component.literal("\n")
|
||||
.setStyle(Style.EMPTY.withBold(false)
|
||||
.withUnderlined(true)
|
||||
.withColor(ChatFormatting.WHITE));
|
||||
ctx.getSource().sendSuccess(footer, false);
|
||||
|
||||
if (withUI && BCLib.isClient() && Configs.CLIENT_CONFIG.showUpdateInfo() && !VersionChecker.isEmpty()) {
|
||||
Minecraft.getInstance().setScreen(new UpdatesScreen(Minecraft.getInstance().screen));
|
||||
}
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class BiomesConfig extends PathConfig {
|
|||
private static final BiomeAPI.BiomeType[] excludeTypes = {BiomeAPI.BiomeType.NETHER, BiomeAPI.BiomeType.END};
|
||||
|
||||
public BiomesConfig() {
|
||||
super(BCLib.MOD_ID, "biomes", false);
|
||||
super(BCLib.MOD_ID, "biomes", true);
|
||||
for (var type : includeTypes) {
|
||||
keeper.registerEntry(
|
||||
new ConfigKey(type.getName(), "force_include"),
|
||||
|
|
58
src/main/java/org/betterx/bclib/config/CachedConfig.java
Normal file
58
src/main/java/org/betterx/bclib/config/CachedConfig.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package org.betterx.bclib.config;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.networking.VersionChecker;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Base64;
|
||||
|
||||
public class CachedConfig extends NamedPathConfig {
|
||||
|
||||
@ConfigUI(hide = true)
|
||||
public static final ConfigToken<String> LAST_CHECK_DATE = ConfigToken.String(
|
||||
"never",
|
||||
"last",
|
||||
"version"
|
||||
);
|
||||
|
||||
@ConfigUI(hide = true)
|
||||
public static final ConfigToken<String> LAST_JSON = ConfigToken.String(
|
||||
"",
|
||||
"cached",
|
||||
"version"
|
||||
);
|
||||
|
||||
public CachedConfig() {
|
||||
super(BCLib.MOD_ID, "cache", false, false);
|
||||
}
|
||||
|
||||
public String lastVersionJson() {
|
||||
byte[] decodedBytes = Base64.getUrlDecoder().decode(get(LAST_JSON));
|
||||
return new String(decodedBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public void setLastVersionJson(String json) {
|
||||
set(LAST_JSON, Base64.getUrlEncoder().encodeToString(json.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public Instant lastCheckDate() {
|
||||
String d = get(LAST_CHECK_DATE);
|
||||
if (d.trim().toLowerCase().equals("never")) {
|
||||
return Instant.now().minus(VersionChecker.WAIT_FOR_DAYS + 1, ChronoUnit.DAYS);
|
||||
}
|
||||
return Instant.parse(d);
|
||||
}
|
||||
|
||||
public void setLastCheckDate() {
|
||||
set(LAST_CHECK_DATE, Instant.now().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges() {
|
||||
synchronized (this) {
|
||||
super.saveChanges();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,11 +31,10 @@ public class ClientConfig extends NamedPathConfig {
|
|||
);
|
||||
@ConfigUI(leftPadding = 8)
|
||||
public static final DependendConfigToken<Boolean> ACCEPT_MODS = DependendConfigToken.Boolean(
|
||||
false,
|
||||
true,
|
||||
"acceptMods",
|
||||
AutoSync.SYNC_CATEGORY,
|
||||
(config) -> config.get(
|
||||
ENABLED)
|
||||
(config) -> config.get(ENABLED)
|
||||
);
|
||||
@ConfigUI(leftPadding = 8)
|
||||
public static final DependendConfigToken<Boolean> DISPLAY_MOD_INFO = DependendConfigToken.Boolean(
|
||||
|
@ -72,6 +71,19 @@ public class ClientConfig extends NamedPathConfig {
|
|||
"rendering"
|
||||
);
|
||||
|
||||
public static final ConfigToken<Boolean> SHOW_UPDATE_INFO = ConfigToken.Boolean(
|
||||
true,
|
||||
"showUpdateInfo",
|
||||
"ui"
|
||||
);
|
||||
|
||||
@ConfigUI(leftPadding = 8)
|
||||
public static final ConfigToken<Boolean> NO_DONOR = ConfigToken.Boolean(
|
||||
false,
|
||||
"no_donor",
|
||||
"version"
|
||||
);
|
||||
|
||||
public ClientConfig() {
|
||||
super(BCLib.MOD_ID, "client", false);
|
||||
}
|
||||
|
@ -112,6 +124,14 @@ public class ClientConfig extends NamedPathConfig {
|
|||
return get(CUSTOM_FOG_RENDERING);
|
||||
}
|
||||
|
||||
public boolean showUpdateInfo() {
|
||||
return get(SHOW_UPDATE_INFO);
|
||||
}
|
||||
|
||||
public boolean isDonor() {
|
||||
return !get(NO_DONOR);
|
||||
}
|
||||
|
||||
public float fogDensity() {
|
||||
return get(FOG_DENSITY);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ public class Configs {
|
|||
|
||||
public static final GeneratorConfig GENERATOR_CONFIG = new GeneratorConfig();
|
||||
public static final MainConfig MAIN_CONFIG = new MainConfig();
|
||||
public static final CachedConfig CACHED_CONFIG = new CachedConfig();
|
||||
|
||||
public static final PathConfig RECIPE_CONFIG = new PathConfig(BCLib.MOD_ID, "recipes");
|
||||
public static final BiomesConfig BIOMES_CONFIG = new BiomesConfig();
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.betterx.bclib.BCLib;
|
|||
|
||||
public class GeneratorConfig extends NamedPathConfig {
|
||||
public GeneratorConfig() {
|
||||
super(BCLib.MOD_ID, "generator", false);
|
||||
super(BCLib.MOD_ID, "generator", true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,22 @@ public class MainConfig extends NamedPathConfig {
|
|||
APPLY_PATCHES)
|
||||
);
|
||||
|
||||
|
||||
@ConfigUI(hide = true)
|
||||
public static final ConfigToken<Boolean> DID_SHOW_WELCOME = ConfigToken.Boolean(
|
||||
false,
|
||||
"did_show_welcome",
|
||||
"version"
|
||||
);
|
||||
|
||||
public static final ConfigToken<Boolean> CHECK_VERSIONS = DependendConfigToken.Boolean(
|
||||
true,
|
||||
"check",
|
||||
"version",
|
||||
(config) -> !config.get(DID_SHOW_WELCOME)
|
||||
);
|
||||
|
||||
|
||||
public MainConfig() {
|
||||
super(BCLib.MOD_ID, "main", true, true);
|
||||
}
|
||||
|
@ -29,4 +45,21 @@ public class MainConfig extends NamedPathConfig {
|
|||
public boolean repairBiomes() {
|
||||
return get(REPAIR_BIOMES);
|
||||
}
|
||||
|
||||
public boolean checkVersions() {
|
||||
return get(CHECK_VERSIONS);
|
||||
}
|
||||
|
||||
public boolean didShowWelcomeScreen() {
|
||||
return get(DID_SHOW_WELCOME);
|
||||
}
|
||||
|
||||
public void setDidShowWelcomeScreen() {
|
||||
set(DID_SHOW_WELCOME, true);
|
||||
}
|
||||
|
||||
|
||||
public void setCheckVersions(boolean newValue) {
|
||||
set(CHECK_VERSIONS, newValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,7 @@ public class ServerConfig extends NamedPathConfig {
|
|||
true,
|
||||
"offerConfigs",
|
||||
AutoSync.SYNC_CATEGORY,
|
||||
(config) -> config.get(
|
||||
ENABLED)
|
||||
(config) -> config.get(ENABLED)
|
||||
);
|
||||
public static final DependendConfigToken<Boolean> OFFER_FILES = DependendConfigToken.Boolean(
|
||||
true,
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.betterx.bclib.mixin.client;
|
||||
|
||||
import org.betterx.bclib.interfaces.CustomColorProvider;
|
||||
import org.betterx.bclib.networking.VersionCheckerClient;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.block.BlockColors;
|
||||
import net.minecraft.client.color.item.ItemColors;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.main.GameConfig;
|
||||
import net.minecraft.core.Registry;
|
||||
|
||||
|
@ -15,6 +17,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
public abstract class MinecraftMixin {
|
||||
@Final
|
||||
|
@ -25,6 +29,11 @@ public abstract class MinecraftMixin {
|
|||
@Shadow
|
||||
private ItemColors itemColors;
|
||||
|
||||
|
||||
@Shadow
|
||||
@Nullable
|
||||
public Screen screen;
|
||||
|
||||
@Inject(method = "<init>*", at = @At("TAIL"))
|
||||
private void bclib_onMCInit(GameConfig args, CallbackInfo info) {
|
||||
Registry.BLOCK.forEach(block -> {
|
||||
|
@ -33,5 +42,7 @@ public abstract class MinecraftMixin {
|
|||
itemColors.register(provider.getItemProvider(), block.asItem());
|
||||
}
|
||||
});
|
||||
|
||||
VersionCheckerClient.presentUpdateScreen(screen);
|
||||
}
|
||||
}
|
||||
|
|
149
src/main/java/org/betterx/bclib/networking/VersionChecker.java
Normal file
149
src/main/java/org/betterx/bclib/networking/VersionChecker.java
Normal file
|
@ -0,0 +1,149 @@
|
|||
package org.betterx.bclib.networking;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.config.Configs;
|
||||
import org.betterx.worlds.together.util.ModUtil;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class VersionChecker implements Runnable {
|
||||
@FunctionalInterface
|
||||
public interface UpdateInfoProvider {
|
||||
void send(String modID, String currentVersion, String newVersion);
|
||||
}
|
||||
|
||||
private static final List<String> KNOWN_MODS = new LinkedList<>();
|
||||
private static final List<ModVersion> NEW_VERSIONS = new LinkedList<>();
|
||||
|
||||
public static class ModVersion {
|
||||
String n;
|
||||
String v;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return n + ":" + v;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Versions {
|
||||
String mc;
|
||||
String loader;
|
||||
List<ModVersion> mods;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Versions{" +
|
||||
"mc='" + mc + '\'' +
|
||||
", loader='" + loader + '\'' +
|
||||
", mods=" + mods +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static final int WAIT_FOR_DAYS = 5;
|
||||
private static final String BASE_URL = "https://wunderreich.ambertation.de/api/v1/versions/";
|
||||
private static Thread versionChecker;
|
||||
|
||||
public static void startCheck(boolean isClient) {
|
||||
if (versionChecker == null) {
|
||||
if (Configs.MAIN_CONFIG.checkVersions()) {
|
||||
versionChecker = new Thread(isClient ? new VersionCheckerClient() : new VersionChecker());
|
||||
versionChecker.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerMod(String modID) {
|
||||
KNOWN_MODS.add(modID);
|
||||
}
|
||||
|
||||
boolean needRecheck() {
|
||||
Instant lastCheck = Configs.CACHED_CONFIG.lastCheckDate().plus(WAIT_FOR_DAYS, ChronoUnit.DAYS);
|
||||
Instant now = Instant.now();
|
||||
|
||||
return now.isAfter(lastCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Gson gson = new Gson();
|
||||
if (needRecheck()) {
|
||||
String minecraftVersion = ModUtil.getModVersion("minecraft").replace(".", "_");
|
||||
BCLib.LOGGER.info("Check Versions for minecraft=" + minecraftVersion);
|
||||
|
||||
try {
|
||||
String fileName = "mc_fabric_" + URLEncoder.encode(
|
||||
minecraftVersion,
|
||||
StandardCharsets.ISO_8859_1.toString()
|
||||
) + ".json";
|
||||
|
||||
URL url = new URL(BASE_URL + fileName);
|
||||
try (InputStreamReader reader = new InputStreamReader(url.openStream())) {
|
||||
Versions json = gson.fromJson(reader, Versions.class);
|
||||
String str = gson.getAdapter(Versions.class).toJson(json);
|
||||
Configs.CACHED_CONFIG.setLastVersionJson(str);
|
||||
Configs.CACHED_CONFIG.setLastCheckDate();
|
||||
Configs.CACHED_CONFIG.saveChanges();
|
||||
|
||||
processVersions(json);
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
BCLib.LOGGER.error("Failed to encode URL during VersionCheck", e);
|
||||
return;
|
||||
} catch (MalformedURLException e) {
|
||||
BCLib.LOGGER.error("Invalid URL during VersionCheck", e);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
BCLib.LOGGER.error("I/O Error during VersionCheck", e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
String str = Configs.CACHED_CONFIG.lastVersionJson();
|
||||
if (str != null && str.trim().length() > 0) {
|
||||
Versions json = gson.fromJson(str, Versions.class);
|
||||
processVersions(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processVersions(Versions json) {
|
||||
if (json != null) {
|
||||
BCLib.LOGGER.info("Received Version Info for minecraft=" + json.mc + ", loader=" + json.loader);
|
||||
if (json.mods != null) {
|
||||
for (ModVersion mod : json.mods) {
|
||||
if (mod.n != null && mod.v != null && KNOWN_MODS.contains(mod.n)) {
|
||||
boolean isNew = ModUtil.isLargerVersion(mod.v, ModUtil.getModVersion(mod.n));
|
||||
BCLib.LOGGER.info(" - " + mod.n + ":" + mod.v + (isNew ? " (update available)" : ""));
|
||||
if (isNew)
|
||||
NEW_VERSIONS.add(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BCLib.LOGGER.warning("No valid Version Info");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEmpty() {
|
||||
return NEW_VERSIONS.isEmpty();
|
||||
}
|
||||
|
||||
public static void forEachUpdate(UpdateInfoProvider consumer) {
|
||||
for (ModVersion v : NEW_VERSIONS) {
|
||||
String currrent = ModUtil.getModVersion(v.n);
|
||||
consumer.send(v.n, currrent, v.v);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package org.betterx.bclib.networking;
|
||||
|
||||
import org.betterx.bclib.client.gui.screens.UpdatesScreen;
|
||||
import org.betterx.bclib.client.gui.screens.WelcomeScreen;
|
||||
import org.betterx.bclib.config.Configs;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class VersionCheckerClient extends VersionChecker {
|
||||
|
||||
public static void presentUpdateScreen(Screen parent) {
|
||||
if (!Configs.MAIN_CONFIG.didShowWelcomeScreen()) {
|
||||
Minecraft.getInstance().setScreen(new WelcomeScreen(parent));
|
||||
|
||||
} else if (Configs.CLIENT_CONFIG.showUpdateInfo() && !VersionChecker.isEmpty()) {
|
||||
Minecraft.getInstance().setScreen(new UpdatesScreen(parent));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,12 +10,24 @@ import org.betterx.ui.layout.values.Value;
|
|||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.components.MultiLineLabel;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
record LineWithWidth(FormattedCharSequence text, int width) {
|
||||
}
|
||||
|
||||
public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRenderer, MultiLineText> {
|
||||
net.minecraft.network.chat.Component text;
|
||||
int color = ColorUtil.DEFAULT_TEXT;
|
||||
protected MultiLineLabel multiLineLabel;
|
||||
int bufferedContentWidth = 0;
|
||||
List<LineWithWidth> lines = List.of();
|
||||
|
||||
public MultiLineText(
|
||||
Value width,
|
||||
|
@ -33,10 +45,25 @@ public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRe
|
|||
return this;
|
||||
}
|
||||
|
||||
public static Component parse(Component text) {
|
||||
String[] parts = text.getString().split("\\*\\*");
|
||||
if (parts.length > 0) {
|
||||
boolean bold = false;
|
||||
MutableComponent c = Component.literal(parts[0]);
|
||||
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
bold = !bold;
|
||||
c.append(Component.literal(parts[i]).setStyle(Style.EMPTY.withBold(bold)));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public MultiLineText setText(Component text) {
|
||||
this.text = text;
|
||||
this.updatedContentWidth();
|
||||
|
||||
|
||||
if (multiLineLabel != null) {
|
||||
multiLineLabel = createVanillaComponent();
|
||||
}
|
||||
|
@ -45,11 +72,14 @@ public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRe
|
|||
}
|
||||
|
||||
protected MultiLineLabel createVanillaComponent() {
|
||||
return MultiLineLabel.create(
|
||||
renderer.getFont(),
|
||||
text,
|
||||
relativeBounds == null ? width.calculatedSize() : relativeBounds.width
|
||||
);
|
||||
final int wd = relativeBounds == null ? width.calculatedSize() : relativeBounds.width;
|
||||
lines = renderer.getFont()
|
||||
.split(text, wd)
|
||||
.stream()
|
||||
.map((component) -> new LineWithWidth(component, renderer.getFont().width(component)))
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
|
||||
return MultiLineLabel.create(renderer.getFont(), text, wd);
|
||||
}
|
||||
|
||||
protected void updatedContentWidth() {
|
||||
|
@ -108,7 +138,6 @@ public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRe
|
|||
Rectangle clipRect
|
||||
) {
|
||||
if (linkedComponent != null && linkedComponent.multiLineLabel != null) {
|
||||
|
||||
int top = bounds.height - getHeight(linkedComponent.text);
|
||||
if (linkedComponent.vAlign == Alignment.MIN) top = 0;
|
||||
if (linkedComponent.vAlign == Alignment.CENTER) top /= 2;
|
||||
|
@ -119,6 +148,20 @@ public class MultiLineText extends LayoutComponent<MultiLineText.MultiLineTextRe
|
|||
getLineHeight(linkedComponent.text),
|
||||
linkedComponent.color
|
||||
);
|
||||
} else if (linkedComponent.hAlign == Alignment.MAX) {
|
||||
int lineY = 0;
|
||||
int lineHeight = getLineHeight(linkedComponent.text);
|
||||
|
||||
for (Iterator<LineWithWidth> iter = linkedComponent.lines.iterator(); iter.hasNext(); lineY += lineHeight) {
|
||||
LineWithWidth textWithWidth = iter.next();
|
||||
getFont().drawShadow(
|
||||
stack,
|
||||
textWithWidth.text(),
|
||||
linkedComponent.width.calculatedSize() - textWithWidth.width(),
|
||||
lineY,
|
||||
linkedComponent.color
|
||||
);
|
||||
}
|
||||
} else {
|
||||
linkedComponent.multiLineLabel.renderLeftAligned(
|
||||
stack, 0, top,
|
||||
|
|
|
@ -75,7 +75,7 @@ public class Text extends LayoutComponent<Text.TextRenderer, Text> {
|
|||
|
||||
int top = bounds.height - getLineHeight(linkedComponent.text);
|
||||
if (linkedComponent.vAlign == Alignment.MIN) top = 0;
|
||||
if (linkedComponent.vAlign == Alignment.CENTER) top /= 2;
|
||||
if (linkedComponent.vAlign == Alignment.CENTER) top = top / 2 + 1;
|
||||
|
||||
GuiComponent.drawString(stack, getFont(), linkedComponent.text, left, top, linkedComponent.color);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.gui.screens.ConfirmLinkScreen;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
@ -138,4 +139,12 @@ public abstract class LayoutScreen extends Screen {
|
|||
public static Value relative(double percentage) {
|
||||
return Value.relative(percentage);
|
||||
}
|
||||
|
||||
public static MutableComponent translatable(String key) {
|
||||
return Component.translatable(key);
|
||||
}
|
||||
|
||||
public static MutableComponent literal(String content) {
|
||||
return Component.literal(content);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue