Skip to content

Commit

Permalink
Split client and server configs (unfinished)
Browse files Browse the repository at this point in the history
  • Loading branch information
mschae23 committed Nov 28, 2024
1 parent 731981f commit cb28bf4
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 21 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ dependencies {
[
"fabric-api-base",
"fabric-registry-sync-v0",
"fabric-lifecycle-events-v1"
"fabric-lifecycle-events-v1",
"fabric-networking-api-v1"
].forEach { modImplementation fabricApi.module(it, project.fabric_api_version) }

modCompileOnly "io.github.fourmisain:TaxFreeLevels-fabric:$project.tax_free_levels_version"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ org.gradle.jvmargs=-Xmx1G

# Dependencies
fabric_api_version=0.110.0+1.21.4
codec_config_api_version=2.1.0+1.21.3
codec_config_api_version=3.0.0+1.21.3
tax_free_levels_version=1.4.1-fabric-1.21.1
113 changes: 95 additions & 18 deletions src/main/java/de/mschae23/grindenchantments/GrindEnchantmentsMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,28 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import net.minecraft.registry.RegistryOps;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.util.Identifier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.loader.api.FabricLoader;
import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.MapCodec;
import de.mschae23.config.api.ConfigIo;
import de.mschae23.config.api.ModConfig;
import de.mschae23.config.api.exception.ConfigException;
import de.mschae23.config.impl.ConfigUtil;
import de.mschae23.grindenchantments.config.ClientConfig;
import de.mschae23.grindenchantments.config.ServerConfig;
import de.mschae23.grindenchantments.config.legacy.v1.GrindEnchantmentsConfigV1;
import de.mschae23.grindenchantments.config.legacy.v2.GrindEnchantmentsConfigV2;
import de.mschae23.grindenchantments.config.legacy.v3.GrindEnchantmentsConfigV3;
Expand All @@ -43,6 +56,7 @@
import de.mschae23.grindenchantments.impl.DisenchantOperation;
import de.mschae23.grindenchantments.impl.MoveOperation;
import de.mschae23.grindenchantments.impl.ResetRepairCostOperation;
import de.mschae23.grindenchantments.network.s2c.ServerConfigS2CPayload;
import de.mschae23.grindenchantments.registry.GrindEnchantmentsRegistries;
import io.github.fourmisain.taxfreelevels.TaxFreeLevels;
import org.apache.logging.log4j.Level;
Expand All @@ -55,9 +69,32 @@ public class GrindEnchantmentsMod implements ModInitializer {
public static final Logger LOGGER = LogManager.getLogger("Grind Enchantments");

private static GrindEnchantmentsConfigV3 LEGACY_CONFIG = GrindEnchantmentsConfigV3.DEFAULT;
@Nullable
private static ServerConfig SERVER_CONFIG = null;
@Nullable
private static ClientConfig CLIENT_CONFIG = null;

@Override
public void onInitialize() {
// Singleplayer
ServerLifecycleEvents.SERVER_STARTING.register(server -> readServerConfig(server.getRegistryManager())
.ifPresent(config -> SERVER_CONFIG = config));
ServerLifecycleEvents.SERVER_STOPPING.register(server -> SERVER_CONFIG = null);

// Multiplayer
PayloadTypeRegistry.configurationS2C().register(ServerConfigS2CPayload.ID, ServerConfigS2CPayload.CODEC);

ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
CLIENT_CONFIG = GrindEnchantmentsMod.readClientConfig().orElse(ClientConfig.DEFAULT);

ClientConfigurationNetworking.registerGlobalReceiver(ServerConfigS2CPayload.ID, (payload, context) -> {
//noinspection resource
context.client().execute(() -> {
// TODO
});
});
});

LEGACY_CONFIG = readLegacyConfig().orElse(GrindEnchantmentsConfigV3.DEFAULT);

GrindEnchantmentsRegistries.init();
Expand Down Expand Up @@ -85,29 +122,74 @@ public void onInitialize() {
}
}

private static <T extends ModConfig<T>> ModConfig.Type<T, ?> getConfigType(int versionOffset, MapCodec<? extends ModConfig<T>>[] codecs, int version) {
for (int i = codecs.length; i > 0; i--) {
if (version == i) {
return new ModConfig.Type<>(i + versionOffset, codecs[i - 1]);
public static ServerConfig getServerConfig() {
return SERVER_CONFIG == null ? ServerConfig.DEFAULT : SERVER_CONFIG;
}

public static ClientConfig getClientConfig() {
return CLIENT_CONFIG == null ? ClientConfig.DEFAULT : CLIENT_CONFIG;
}

@Deprecated
public static GrindEnchantmentsConfigV3 getLegacyConfig() {
return LEGACY_CONFIG;
}

private static <C extends ModConfig<C>> ModConfig.Type<C, ? extends ModConfig<C>> getConfigType(ModConfig.Type<C, ? extends ModConfig<C>>[] versions, int version) {
for (int i = versions.length - 1; i >= 0; i--) {
ModConfig.Type<C, ? extends ModConfig<C>> v = versions[i];

if (version == v.version()) {
return v;
}
}

return versions[versions.length - 1];
}

private static <C extends ModConfig<C>> Optional<C> readGenericConfig(Path configName, Codec<ModConfig<C>> codec,
DynamicOps<JsonElement> ops, String kind) {
Path filePath = FabricLoader.getInstance().getConfigDir().resolve(MODID).resolve(configName);
@Nullable
C config = null;

if (Files.exists(filePath) && Files.isRegularFile(filePath)) {
try (InputStream input = Files.newInputStream(filePath)) {
log(Level.INFO, "Reading " + kind + " config.");

ModConfig<C> readConfig = ConfigIo.decodeConfig(input, codec, ops);
config = readConfig.latest();
} catch (IOException e) {
log(Level.ERROR, "IO exception while trying to read " + kind + " config: " + e.getLocalizedMessage());
} catch (ConfigException e) {
log(Level.ERROR, e.getLocalizedMessage());
}
}

return new ModConfig.Type<>(codecs.length + versionOffset, codecs[codecs.length - 1]);
return Optional.ofNullable(config);
}

public static Optional<ClientConfig> readClientConfig() {
return readGenericConfig(Path.of("client.json"), ModConfig.<ClientConfig>createCodec(ClientConfig.TYPE.version(), version ->
getConfigType(ClientConfig.VERSIONS, version)), JsonOps.INSTANCE, "client");
}

private static Optional<ServerConfig> readServerConfig(RegistryWrapper.WrapperLookup wrapperLookup) {
return readGenericConfig(Path.of("server.json"), ModConfig.<ServerConfig>createCodec(ServerConfig.TYPE.version(), version ->
getConfigType(ServerConfig.VERSIONS, version)), RegistryOps.of(JsonOps.INSTANCE, wrapperLookup), "server");
}

@SuppressWarnings("deprecation")
private static Optional<GrindEnchantmentsConfigV3> readLegacyConfig() {
final GrindEnchantmentsConfigV3 legacyLatestConfigDefault = GrindEnchantmentsConfigV3.DEFAULT;
final int legacyLatestConfigVersion = legacyLatestConfigDefault.version();
@SuppressWarnings({"unchecked", "deprecation"})
final MapCodec<? extends ModConfig<GrindEnchantmentsConfigV3>>[] legacyConfigCodecs = new MapCodec[] {
GrindEnchantmentsConfigV1.TYPE_CODEC, GrindEnchantmentsConfigV2.TYPE_CODEC, GrindEnchantmentsConfigV3.TYPE_CODEC
final ModConfig.Type<GrindEnchantmentsConfigV3, ? extends ModConfig<GrindEnchantmentsConfigV3>>[] legacyConfigCodecs = new ModConfig.Type[] {
GrindEnchantmentsConfigV1.TYPE, GrindEnchantmentsConfigV2.TYPE, GrindEnchantmentsConfigV3.TYPE
};

final Codec<ModConfig<GrindEnchantmentsConfigV3>> legacyConfigCodec = ModConfig.createCodec(legacyLatestConfigVersion, version ->
getConfigType(0, legacyConfigCodecs, version));

// Unfortunately, this requires some manual work and usage of codec config API's internals
getConfigType(legacyConfigCodecs, version));

Path configPath = FabricLoader.getInstance().getConfigDir().resolve(Paths.get(MODID + ".json"));
@Nullable
Expand All @@ -117,8 +199,7 @@ private static Optional<GrindEnchantmentsConfigV3> readLegacyConfig() {
try (InputStream input = Files.newInputStream(configPath)) {
log(Level.INFO, "Reading legacy config.");

@SuppressWarnings("UnstableApiUsage")
ModConfig<GrindEnchantmentsConfigV3> readConfig = ConfigUtil.decodeConfig(input, legacyConfigCodec, JsonOps.INSTANCE);
ModConfig<GrindEnchantmentsConfigV3> readConfig = ConfigIo.decodeConfig(input, legacyConfigCodec, JsonOps.INSTANCE);
config = readConfig.latest();
} catch (IOException e) {
log(Level.ERROR, "IO exception while trying to read config: " + e.getLocalizedMessage());
Expand All @@ -130,10 +211,6 @@ private static Optional<GrindEnchantmentsConfigV3> readLegacyConfig() {
return Optional.ofNullable(config);
}

public static GrindEnchantmentsConfigV3 getConfig() {
return LEGACY_CONFIG;
}

public static void log(Level level, Object message) {
LOGGER.log(level, "[Grind Enchantments] {}", message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 mschae23
*
* This file is part of Grind enchantments.
*
* Grind enchantments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package de.mschae23.grindenchantments.config;

import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import de.mschae23.config.api.ModConfig;

public record ClientConfig(boolean showLevelCost) implements ModConfig<ClientConfig> {
public static final MapCodec<ClientConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
Codec.BOOL.fieldOf("show_enchantment_cost").forGetter(ClientConfig::showLevelCost)
).apply(instance, instance.stable(ClientConfig::new)));

public static final ModConfig.Type<ClientConfig, ClientConfig> TYPE = new ModConfig.Type<>(4, CODEC);
public static final ClientConfig DEFAULT = new ClientConfig(true);

@SuppressWarnings("unchecked")
public static final ModConfig.Type<ClientConfig, ? extends ModConfig<ClientConfig>>[] VERSIONS = new ModConfig.Type[] { TYPE, };

@Override
public Type<ClientConfig, ?> type() {
return TYPE;
}

@Override
public ClientConfig latest() {
return this;
}

@Override
public boolean shouldUpdate() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2024 mschae23
*
* This file is part of Grind enchantments.
*
* Grind enchantments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package de.mschae23.grindenchantments.config;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;

public record DedicatedServerConfig(boolean alternativeCostDisplay) {
public static final Codec<DedicatedServerConfig> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.BOOL.fieldOf("alternative_cost_display_enabled").forGetter(DedicatedServerConfig::alternativeCostDisplay)
).apply(instance, instance.stable(DedicatedServerConfig::new)));

public static final DedicatedServerConfig DEFAULT = new DedicatedServerConfig(false);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2024 mschae23
*
* This file is part of Grind enchantments.
*
* Grind enchantments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package de.mschae23.grindenchantments.config;

import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import de.mschae23.config.api.ModConfig;

public record ServerConfig(DisenchantConfig disenchant, MoveConfig move, ResetRepairCostConfig resetRepairCost,
FilterConfig filter,
DedicatedServerConfig dedicatedServerConfig) implements ModConfig<ServerConfig> {
public static final MapCodec<ServerConfig> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
DisenchantConfig.CODEC.fieldOf("disenchant_to_book").forGetter(ServerConfig::disenchant),
MoveConfig.CODEC.fieldOf("move_enchantments").forGetter(ServerConfig::move),
ResetRepairCostConfig.CODEC.fieldOf("reset_repair_cost").forGetter(ServerConfig::resetRepairCost),
FilterConfig.CODEC.fieldOf("filter").forGetter(ServerConfig::filter),
DedicatedServerConfig.CODEC.orElse(DedicatedServerConfig.DEFAULT).fieldOf("dedicated_server_options").forGetter(ServerConfig::dedicatedServerConfig)
).apply(instance, instance.stable(ServerConfig::new)));

public static final ModConfig.Type<ServerConfig, ServerConfig> TYPE = new ModConfig.Type<>(4, CODEC);
public static final ServerConfig DEFAULT = new ServerConfig(DisenchantConfig.DEFAULT, MoveConfig.DEFAULT,
ResetRepairCostConfig.DEFAULT, FilterConfig.DEFAULT, DedicatedServerConfig.DEFAULT);

@SuppressWarnings("unchecked")
public static final ModConfig.Type<ServerConfig, ? extends ModConfig<ServerConfig>>[] VERSIONS = new ModConfig.Type[] { TYPE, };

@Override
public Type<ServerConfig, ?> type() {
return TYPE;
}

@Override
public ServerConfig latest() {
return this;
}

@Override
public boolean shouldUpdate() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2024 mschae23
*
* This file is part of Grind enchantments.
*
* Grind enchantments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package de.mschae23.grindenchantments.network.s2c;

import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier;
import de.mschae23.grindenchantments.GrindEnchantmentsMod;

public record ServerConfigS2CPayload() implements CustomPayload {
public static final Identifier PACKET_ID = GrindEnchantmentsMod.id("server_config");
public static final CustomPayload.Id<ServerConfigS2CPayload> ID = new CustomPayload.Id<>(PACKET_ID);

public static final PacketCodec<PacketByteBuf, ServerConfigS2CPayload> CODEC = PacketCodec.unit(new ServerConfigS2CPayload());

@Override
public Id<? extends CustomPayload> getId() {
return ID;
}
}
4 changes: 3 additions & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"java": ">=21",

"fabric-api-base": "*",
"fabric-registry-sync-v0": "*"
"fabric-registry-sync-v0": "*",
"fabric-lifecycle-events-v1": "*",
"fabric-networking-api-v1": "*"
},
"suggests": {
},
Expand Down

0 comments on commit cb28bf4

Please sign in to comment.