diff --git a/build.gradle b/build.gradle index 7a63641..cce49c7 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,10 @@ fabricApi { } repositories { + maven { + name = "Terraformers" + url = "https://maven.terraformersmc.com/" + } // Add repositories to retrieve artifacts from in here. // You should only use this when depending on other mods because // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. @@ -43,6 +47,8 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") } processResources { diff --git a/gradle.properties b/gradle.properties index 971fa9a..a74a1b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,9 +6,10 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loader_version=0.18.3 # Mod Properties -mod_version=26.3.11.1 +mod_version=26.3.11.2 maven_group=dev.tggamesyt archives_base_name=szar # Dependencies # check this on https://modmuss50.me/fabric.html fabric_version=0.92.6+1.20.1 +modmenu_version=7.2.2 diff --git a/src/client/java/dev/tggamesyt/szar/client/ConfigEntry.java b/src/client/java/dev/tggamesyt/szar/client/ConfigEntry.java new file mode 100644 index 0000000..97823e5 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ConfigEntry.java @@ -0,0 +1,27 @@ +package dev.tggamesyt.szar.client; + +public class ConfigEntry { + public final String id; + public final String displayName; + public final boolean defaultValue; + public final String linkedResourcePack; // null if no resourcepack linked + + private boolean value; + + public ConfigEntry(String id, String displayName, boolean defaultValue, String linkedResourcePack) { + this.id = id; + this.displayName = displayName; + this.defaultValue = defaultValue; + this.linkedResourcePack = linkedResourcePack; + this.value = defaultValue; + } + + // Convenience constructor — no resourcepack + public ConfigEntry(String id, String displayName, boolean defaultValue) { + this(id, displayName, defaultValue, null); + } + + public boolean get() { return value; } + public void set(boolean v) { value = v; } + public boolean hasResourcePack() { return linkedResourcePack != null; } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/ConfigPreset.java b/src/client/java/dev/tggamesyt/szar/client/ConfigPreset.java new file mode 100644 index 0000000..28ff8ff --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ConfigPreset.java @@ -0,0 +1,17 @@ +package dev.tggamesyt.szar.client; + +import java.util.Map; + +public class ConfigPreset { + public final String id; + public final String displayName; + public final Map values; // setting id → value, null means "leave as-is" (custom) + + public ConfigPreset(String id, String displayName, Map values) { + this.id = id; + this.displayName = displayName; + this.values = values; // null map = Custom preset + } + + public boolean isCustom() { return values == null; } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/ConfigScreen.java b/src/client/java/dev/tggamesyt/szar/client/ConfigScreen.java new file mode 100644 index 0000000..9bcc2dd --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ConfigScreen.java @@ -0,0 +1,145 @@ +package dev.tggamesyt.szar.client; + +import dev.tggamesyt.szar.Szar; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.text.Text; + +import java.util.*; + +public class ConfigScreen extends Screen { + + private final Screen parent; + private final List presetButtons = new ArrayList<>(); + private final List toggleButtons = new ArrayList<>(); + + public ConfigScreen(Screen parent) { + super(Text.literal("Your Mod Config")); + this.parent = parent; + } + + @Override + protected void init() { + int cx = width / 2; + int y = 40; + + // ── Preset buttons ──────────────────────────────────────────────────── + List presets = new ArrayList<>(ModConfig.allPresets()); + int btnW = Math.min(70, (width - 40) / presets.size() - 4); + int total = presets.size() * (btnW + 4); + int px = cx - total / 2; + + presetButtons.clear(); + for (ConfigPreset preset : presets) { + int bx = px; + ButtonWidget btn = ButtonWidget.builder( + presetButtonLabel(preset), + b -> { + ModConfig.applyPreset(preset.id); + refreshPresetButtons(); + refreshToggles(); + }) + .dimensions(bx, y, btnW, 20) + .build(); + addDrawableChild(btn); + presetButtons.add(btn); + px += btnW + 4; + } + y += 34; + + // ── Toggle buttons — generated from registered settings ─────────────── + toggleButtons.clear(); + for (ConfigEntry entry : ModConfig.allSettings()) { + ButtonWidget btn = ButtonWidget.builder( + toggleLabel(entry), + b -> { + ModConfig.setAndMarkCustom(entry.id, !entry.get()); + b.setMessage(toggleLabel(entry)); + refreshPresetButtons(); // update custom highlight + }) + .dimensions(cx - 100, y, 200, 20) + .build(); + addDrawableChild(btn); + toggleButtons.add(btn); + y += 26; + } + + refreshPresetButtons(); + refreshToggles(); + + // ── Done ────────────────────────────────────────────────────────────── + addDrawableChild(ButtonWidget.builder(Text.literal("Done"), b -> { + ModConfig.save(); + ResourcePackHelper.applyAll(client); + PacketByteBuf buf = PacketByteBufs.create(); + + // Write each setting as: id (string), value (boolean) + var settings = ModConfig.allSettings(); + buf.writeInt(settings.size()); + for (ConfigEntry entry : settings) { + buf.writeString(entry.id); + buf.writeBoolean(entry.get()); + } + + ClientPlayNetworking.send(Szar.CONFIG_SYNC, buf); + client.setScreen(parent); + }).dimensions(cx - 75, height - 30, 150, 20).build()); + } + + private void refreshPresetButtons() { + List presets = new ArrayList<>(ModConfig.allPresets()); + for (int i = 0; i < presetButtons.size(); i++) { + presetButtons.get(i).setMessage(presetButtonLabel(presets.get(i))); + } + } + + private void refreshToggles() { + boolean isCustom = PRESETS.values().stream() + .filter(ConfigPreset::isCustom) + .anyMatch(p -> p.id.equals(ModConfig.getActivePresetId())); + + List entries = new ArrayList<>(ModConfig.allSettings()); + for (int i = 0; i < toggleButtons.size(); i++) { + ConfigEntry entry = entries.get(i); + toggleButtons.get(i).active = isCustom; + toggleButtons.get(i).setMessage(toggleLabel(entry)); + } + } + + // Grab presets map for isCustom check + private static final java.util.LinkedHashMap PRESETS; + static { + try { + var field = ModConfig.class.getDeclaredField("PRESETS"); + field.setAccessible(true); + //noinspection unchecked + PRESETS = (java.util.LinkedHashMap) field.get(null); + } catch (Exception e) { throw new RuntimeException(e); } + } + + private Text presetButtonLabel(ConfigPreset preset) { + boolean active = preset.id.equals(ModConfig.getActivePresetId()); + return active + ? Text.literal("§e[" + preset.displayName + "]§r") + : Text.literal(preset.displayName); + } + + private Text toggleLabel(ConfigEntry entry) { + return Text.literal(entry.displayName + ": " + (entry.get() ? "§aON§r" : "§cOFF§r")); + } + + @Override + public void render(DrawContext ctx, int mouseX, int mouseY, float delta) { + renderBackground(ctx); + ctx.drawCenteredTextWithShadow(textRenderer, title, width / 2, 15, 0xFFFFFF); + ctx.drawTextWithShadow(textRenderer, Text.literal("§7Preset:"), width / 2 - 130, 43, 0xAAAAAA); + super.render(ctx, mouseX, mouseY, delta); + } + + @Override + public void close() { client.setScreen(parent); } +} diff --git a/src/client/java/dev/tggamesyt/szar/client/ModConfig.java b/src/client/java/dev/tggamesyt/szar/client/ModConfig.java new file mode 100644 index 0000000..212c878 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ModConfig.java @@ -0,0 +1,120 @@ +package dev.tggamesyt.szar.client; + +import com.google.gson.*; +import net.fabricmc.loader.api.FabricLoader; + +import java.io.*; +import java.nio.file.*; +import java.util.*; + +public class ModConfig { + + private static final Path CONFIG_PATH = + FabricLoader.getInstance().getConfigDir().resolve("yourmod.json"); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + // ── Registry ────────────────────────────────────────────────────────────── + private static final LinkedHashMap SETTINGS = new LinkedHashMap<>(); + private static final LinkedHashMap PRESETS = new LinkedHashMap<>(); + private static String activePresetId = null; + + // ── Registration API ────────────────────────────────────────────────────── + + /** + * Register a toggle setting. + * @param id Unique ID, used in save file and code + * @param displayName Label shown in the config screen + * @param defaultValue Default on/off state + * @param resourcePackId Resource pack to enable/disable with this setting, or null + */ + public static ConfigEntry newSetting(String id, String displayName, + boolean defaultValue, String resourcePackId) { + ConfigEntry entry = new ConfigEntry(id, displayName, defaultValue, resourcePackId); + SETTINGS.put(id, entry); + return entry; + } + + /** Register a toggle setting with no linked resource pack. */ + public static ConfigEntry newSetting(String id, String displayName, boolean defaultValue) { + return newSetting(id, displayName, defaultValue, null); + } + + /** + * Register a preset. + * @param id Unique ID + * @param displayName Label shown on the button + * @param values Map of setting id → value. Pass null to make this the "Custom" preset. + */ + public static ConfigPreset newPreset(String id, String displayName, Map values) { + ConfigPreset preset = new ConfigPreset(id, displayName, values); + PRESETS.put(id, preset); + if (activePresetId == null) activePresetId = id; // first preset is default + return preset; + } + + // ── Accessors ───────────────────────────────────────────────────────────── + + public static boolean get(String settingId) { + ConfigEntry e = SETTINGS.get(settingId); + if (e == null) throw new IllegalArgumentException("Unknown setting: " + settingId); + return e.get(); + } + + public static Collection allSettings() { return SETTINGS.values(); } + public static Collection allPresets() { return PRESETS.values(); } + + public static String getActivePresetId() { return activePresetId; } + + public static void applyPreset(String presetId) { + ConfigPreset preset = PRESETS.get(presetId); + if (preset == null) throw new IllegalArgumentException("Unknown preset: " + presetId); + activePresetId = presetId; + if (!preset.isCustom()) { + preset.values.forEach((id, val) -> { + ConfigEntry e = SETTINGS.get(id); + if (e != null) e.set(val); + }); + } + // Custom preset: leave all values as-is + } + + /** Call this when the user manually changes a toggle — auto-switches to the custom preset. */ + public static void setAndMarkCustom(String settingId, boolean value) { + ConfigEntry e = SETTINGS.get(settingId); + if (e == null) throw new IllegalArgumentException("Unknown setting: " + settingId); + e.set(value); + // Find custom preset and switch to it + PRESETS.values().stream() + .filter(ConfigPreset::isCustom) + .findFirst() + .ifPresent(p -> activePresetId = p.id); + } + + // ── Save / Load ─────────────────────────────────────────────────────────── + + public static void save() { + JsonObject root = new JsonObject(); + root.addProperty("preset", activePresetId); + JsonObject values = new JsonObject(); + SETTINGS.forEach((id, entry) -> values.addProperty(id, entry.get())); + root.add("values", values); + try (Writer w = Files.newBufferedWriter(CONFIG_PATH)) { + GSON.toJson(root, w); + } catch (IOException e) { e.printStackTrace(); } + } + + public static void load() { + if (!Files.exists(CONFIG_PATH)) { save(); return; } + try (Reader r = Files.newBufferedReader(CONFIG_PATH)) { + JsonObject root = GSON.fromJson(r, JsonObject.class); + if (root == null) return; + if (root.has("preset")) activePresetId = root.get("preset").getAsString(); + if (root.has("values")) { + JsonObject values = root.getAsJsonObject("values"); + SETTINGS.forEach((id, entry) -> { + if (values.has(id)) entry.set(values.get(id).getAsBoolean()); + }); + } + } catch (IOException e) { e.printStackTrace(); } + } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/ModMenuIntegration.java b/src/client/java/dev/tggamesyt/szar/client/ModMenuIntegration.java new file mode 100644 index 0000000..14ac0a0 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ModMenuIntegration.java @@ -0,0 +1,11 @@ +package dev.tggamesyt.szar.client; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; + +public class ModMenuIntegration implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return ConfigScreen::new; + } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/ModSettings.java b/src/client/java/dev/tggamesyt/szar/client/ModSettings.java new file mode 100644 index 0000000..7e7a59d --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ModSettings.java @@ -0,0 +1,40 @@ +package dev.tggamesyt.szar.client; + +import java.util.Map; + +public class ModSettings { + // ── Declare setting references for easy typed access ────────────────────── + public static ConfigEntry RACIST; + public static ConfigEntry GAMBLING; + public static ConfigEntry NSFW; + // To add a new setting: just add a line here and in init() below. That's it. + + public static void init() { + // newSetting(id, displayName, defaultValue) + // newSetting(id, displayName, defaultValue, "resourcepack/id") + RACIST = ModConfig.newSetting("racist", "Block Racist content", true, "szar:racist"); + GAMBLING = ModConfig.newSetting("gambling", "Block Gambling", true); + NSFW = ModConfig.newSetting("nsfw", "Block NSFW content",true, "szar:nsfw"); + + // ── Presets ─────────────────────────────────────────────────────────── + // newPreset(id, displayName, Map) + // Pass null map for the "custom" preset (user-editable, no fixed values) + + ModConfig.newPreset("none", "18+", Map.of( + "racist", false, + "gambling", false, + "nsfw", false + )); + ModConfig.newPreset("some", "17+", Map.of( + "racist", false, + "gambling", false, + "nsfw", true + )); + ModConfig.newPreset("all", "Minor", Map.of( + "racist", true, + "gambling", true, + "nsfw", true + )); + ModConfig.newPreset("custom", "Custom", null); // null = custom, toggles stay editable + } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/ResourcePackHelper.java b/src/client/java/dev/tggamesyt/szar/client/ResourcePackHelper.java new file mode 100644 index 0000000..3dfa71c --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/ResourcePackHelper.java @@ -0,0 +1,66 @@ +package dev.tggamesyt.szar.client; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.resource.ResourcePackManager; +import net.minecraft.resource.ResourcePackProfile; + +import java.util.*; + +public class ResourcePackHelper { + + public static void applyAll(MinecraftClient client) { + ResourcePackManager manager = client.getResourcePackManager(); + manager.scanPacks(); + + Set original = new HashSet<>( + manager.getEnabledProfiles().stream() + .map(p -> p.getName()) + .toList() + ); + + Set enabledNames = new HashSet<>( + manager.getEnabledProfiles().stream() + .map(p -> p.getName()) + .toList() + ); + + for (ConfigEntry entry : ModConfig.allSettings()) { + if (!entry.hasResourcePack()) continue; + if (entry.get()) { + enabledNames.add(entry.linkedResourcePack); + } else { + enabledNames.remove(entry.linkedResourcePack); + } + } + + if (enabledNames.equals(original)) { + return; + } + + + // Use the manager to set enabled packs properly — this is the key fix + manager.setEnabledProfiles(enabledNames); + + // Sync back to options and save + client.options.resourcePacks.clear(); + client.options.resourcePacks.addAll( + manager.getEnabledProfiles().stream() + .map(p -> p.getName()) + .toList() + ); + client.options.write(); + client.reloadResources(); + } + + public static Set getManagedPacks() { + Set managed = new HashSet<>(); + for (ConfigEntry entry : ModConfig.allSettings()) { + if (entry.hasResourcePack()) managed.add(entry.linkedResourcePack); + } + return managed; + } + + public static boolean isManaged(String packName) { + return getManagedPacks().contains(packName); + } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java index cc93f83..1dd5d23 100644 --- a/src/client/java/dev/tggamesyt/szar/client/SzarClient.java +++ b/src/client/java/dev/tggamesyt/szar/client/SzarClient.java @@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; @@ -16,6 +17,9 @@ import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.fabric.api.resource.ResourcePackActivationType; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ingame.HandledScreens; import net.minecraft.client.network.AbstractClientPlayerEntity; @@ -43,6 +47,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.network.PacketByteBuf; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; +import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.Util; import net.minecraft.util.math.Box; @@ -91,6 +96,34 @@ public class SzarClient implements ClientModInitializer { int loopStart = startOffset + startLength; @Override public void onInitializeClient() { + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { + PacketByteBuf buf = PacketByteBufs.create(); + + // Write each setting as: id (string), value (boolean) + var settings = ModConfig.allSettings(); + buf.writeInt(settings.size()); + for (ConfigEntry entry : settings) { + buf.writeString(entry.id); + buf.writeBoolean(entry.get()); + } + + ClientPlayNetworking.send(Szar.CONFIG_SYNC, buf); + }); + ModSettings.init(); // register all settings & presets FIRST + ModConfig.load(); // then load saved values + + ResourceManagerHelper.registerBuiltinResourcePack( + new Identifier(MOD_ID, "nsfw"), + FabricLoader.getInstance().getModContainer(MOD_ID).get(), + Text.literal("NSFW Censorship"), + ResourcePackActivationType.NORMAL + ); + ResourceManagerHelper.registerBuiltinResourcePack( + new Identifier(MOD_ID, "racist"), + FabricLoader.getInstance().getModContainer(MOD_ID).get(), + Text.literal("Racism Censorship"), + ResourcePackActivationType.NORMAL + ); EntityRendererRegistry.register(Szar.RADIATION_AREA, EmptyEntityRenderer::new); ClientPlayNetworking.registerGlobalReceiver(Szar.PLAY_VIDEO, (client, handler, buf, responseSender) -> { diff --git a/src/client/java/dev/tggamesyt/szar/client/mixin/PackMixin.java b/src/client/java/dev/tggamesyt/szar/client/mixin/PackMixin.java new file mode 100644 index 0000000..809d4a2 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/mixin/PackMixin.java @@ -0,0 +1,30 @@ +package dev.tggamesyt.szar.client.mixin; + +import dev.tggamesyt.szar.client.ResourcePackHelper; +import net.minecraft.client.gui.screen.pack.ResourcePackOrganizer; +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; + +@Mixin(ResourcePackOrganizer.Pack.class) +public interface PackMixin { + + @Shadow + String getName(); + + @Inject(method = "canBeEnabled()Z", at = @At("RETURN"), cancellable = true) + private void onCanBeEnabled(CallbackInfoReturnable cir) { + if (ResourcePackHelper.isManaged(this.getName())) { + cir.setReturnValue(false); + } + } + + @Inject(method = "canBeDisabled()Z", at = @At("RETURN"), cancellable = true) + private void onCanBeDisabled(CallbackInfoReturnable cir) { + if (ResourcePackHelper.isManaged(this.getName())) { + cir.setReturnValue(false); + } + } +} \ No newline at end of file diff --git a/src/client/java/dev/tggamesyt/szar/client/mixin/PackScreenCloseMixin.java b/src/client/java/dev/tggamesyt/szar/client/mixin/PackScreenCloseMixin.java new file mode 100644 index 0000000..0956897 --- /dev/null +++ b/src/client/java/dev/tggamesyt/szar/client/mixin/PackScreenCloseMixin.java @@ -0,0 +1,17 @@ +package dev.tggamesyt.szar.client.mixin; + +import dev.tggamesyt.szar.client.ResourcePackHelper; +import net.minecraft.client.gui.screen.pack.PackScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PackScreen.class) +public class PackScreenCloseMixin { + + @Inject(method = "close", at = @At("HEAD")) + private void onClose(CallbackInfo ci) { + ResourcePackHelper.applyAll(net.minecraft.client.MinecraftClient.getInstance()); + } +} \ No newline at end of file diff --git a/src/client/resources/szar.client.mixins.json b/src/client/resources/szar.client.mixins.json index a0fc4fc..1272752 100644 --- a/src/client/resources/szar.client.mixins.json +++ b/src/client/resources/szar.client.mixins.json @@ -9,6 +9,8 @@ "HeldItemRendererMixin", "ItemRendererMixin", "MouseMixin", + "PackMixin", + "PackScreenCloseMixin", "PlayerEntityRendererMixin", "PlayerHeldItemFeatureRendererMixin", "PlayerModelMixin", diff --git a/src/main/java/dev/tggamesyt/szar/FaszItem.java b/src/main/java/dev/tggamesyt/szar/FaszItem.java index fcb8b16..1b62207 100644 --- a/src/main/java/dev/tggamesyt/szar/FaszItem.java +++ b/src/main/java/dev/tggamesyt/szar/FaszItem.java @@ -15,6 +15,7 @@ import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; @@ -130,6 +131,7 @@ public class FaszItem extends BlockItem { // If the entity is a player → apply special effect logic if (living instanceof PlayerEntity target) { + if (PlayerConfigStore.get(target, "nsfw")) { return;} int chance = 5; // 1/5 default ItemStack offhand = user.getOffHandStack(); if (!offhand.isEmpty() && offhand.isOf(CNDM.getItem())) { diff --git a/src/main/java/dev/tggamesyt/szar/PlayerConfigStore.java b/src/main/java/dev/tggamesyt/szar/PlayerConfigStore.java new file mode 100644 index 0000000..b0c0c2b --- /dev/null +++ b/src/main/java/dev/tggamesyt/szar/PlayerConfigStore.java @@ -0,0 +1,41 @@ +package dev.tggamesyt.szar; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.network.ServerPlayerEntity; +import java.util.*; + +public class PlayerConfigStore { + + // Map of player UUID → (setting id → value) + private static final Map> store = new HashMap<>(); + + public static void set(ServerPlayerEntity player, Map config) { + store.put(player.getUuid(), config); + } + + public static boolean get(ServerPlayerEntity player, String settingId) { + Map config = store.get(player.getUuid()); + if (config == null) return false; // default if not synced yet + return config.getOrDefault(settingId, false); + } + + public static boolean get(PlayerEntity player, String settingId) { + Map config = store.get(player.getUuid()); + if (config == null) return false; // default if not synced yet + return config.getOrDefault(settingId, false); + } + + public static boolean get(UUID uuid, String settingId) { + Map config = store.get(uuid); + if (config == null) return false; + return config.getOrDefault(settingId, false); + } + + public static void remove(ServerPlayerEntity player) { + store.remove(player.getUuid()); + } + + public static boolean hasSynced(ServerPlayerEntity player) { + return store.containsKey(player.getUuid()); + } +} diff --git a/src/main/java/dev/tggamesyt/szar/RouletteBlock.java b/src/main/java/dev/tggamesyt/szar/RouletteBlock.java index 8707e72..f0871e9 100644 --- a/src/main/java/dev/tggamesyt/szar/RouletteBlock.java +++ b/src/main/java/dev/tggamesyt/szar/RouletteBlock.java @@ -103,7 +103,7 @@ public class RouletteBlock extends Block implements BlockEntityProvider { @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - + if (PlayerConfigStore.get(player, "gambling")) {return ActionResult.FAIL;} if (hand != Hand.MAIN_HAND) return ActionResult.PASS; BlockEntity blockEntity = world.getBlockEntity(pos); diff --git a/src/main/java/dev/tggamesyt/szar/SlotMachineBlock.java b/src/main/java/dev/tggamesyt/szar/SlotMachineBlock.java index 37e4b27..4622162 100644 --- a/src/main/java/dev/tggamesyt/szar/SlotMachineBlock.java +++ b/src/main/java/dev/tggamesyt/szar/SlotMachineBlock.java @@ -92,7 +92,7 @@ public class SlotMachineBlock extends Block implements BlockEntityProvider { @Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - + if (PlayerConfigStore.get(player, "gambling")) {return ActionResult.FAIL;} if (hand != Hand.MAIN_HAND) return ActionResult.PASS; BlockEntity blockEntity = world.getBlockEntity(pos); diff --git a/src/main/java/dev/tggamesyt/szar/Szar.java b/src/main/java/dev/tggamesyt/szar/Szar.java index 30a34e6..fc31183 100644 --- a/src/main/java/dev/tggamesyt/szar/Szar.java +++ b/src/main/java/dev/tggamesyt/szar/Szar.java @@ -133,13 +133,15 @@ public class Szar implements ModInitializer { SoundEvent.of(new Identifier(Szar.MOD_ID, "won")) ); public static final SoundEvent MERL_SOUND = - SoundEvent.of(new Identifier("szar", "merl")); + SoundEvent.of(new Identifier(MOD_ID, "merl")); public static final Identifier PLANE_ANIM_PACKET = new Identifier(MOD_ID, "plane_anim"); - public static final Identifier NAZI_HAND_GESTURE = new Identifier("szar", "hit_hand"); + public static final Identifier NAZI_HAND_GESTURE = new Identifier(MOD_ID, "hit_hand"); public static final Identifier OPEN_URL = new Identifier(MOD_ID, "epsteinfiles"); public static final Identifier PLAY_VIDEO = new Identifier(MOD_ID, "play_video"); + public static final Identifier CONFIG_SYNC = new Identifier(MOD_ID, "config_sync"); + public static final Block SZAR_BLOCK = new SzarBlock(); @@ -296,7 +298,7 @@ public class Szar implements ModInitializer { new Identifier(MOD_ID, "szar_group"), FabricItemGroup.builder() .displayName(Text.translatable("itemgroup.szar_group")) - .icon(() -> new ItemStack(Szar.CIGANYBLOCK)) // icon item + .icon(() -> new ItemStack(Szar.CANNABIS_ITEM)) // icon item .entries((displayContext, entries) -> { // drugs entries.add(Szar.CANNABIS_ITEM); @@ -360,6 +362,27 @@ public class Szar implements ModInitializer { private final Map sleepingPlayers = new HashMap<>(); @Override public void onInitialize() { + ServerPlayNetworking.registerGlobalReceiver(CONFIG_SYNC, + (server, player, handler, buf, responseSender) -> { + // Read on netty thread, process on server thread + int count = buf.readInt(); + Map config = new HashMap<>(); + for (int i = 0; i < count; i++) { + String id = buf.readString(); + boolean value = buf.readBoolean(); + config.put(id, value); + } + + // Always handle game state on the server thread + server.execute(() -> { + PlayerConfigStore.set(player, config); + }); + }); + + // Clean up when player leaves + ServerPlayConnectionEvents.DISCONNECT.register( + (handler, server) -> PlayerConfigStore.remove(handler.player) + ); PlayerMovementManager.init(); ServerCosmetics.init(); ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { @@ -1571,6 +1594,7 @@ public class Szar implements ModInitializer { } private void givePregnantEffect(ServerPlayerEntity player, ServerPlayerEntity partner, int chance) { + if (PlayerConfigStore.get(player, "nsfw") || PlayerConfigStore.get(partner, "nsfw")) { return; } if (partner.getOffHandStack().getItem() == Szar.CNDM) { partner.getOffHandStack().decrement(1); partner.dropStack(new ItemStack(WHITE_LIQUID)); diff --git a/src/main/resources/assets/szar/icon.png b/src/main/resources/assets/szar/icon.png index 8cc376b..c44f36c 100644 Binary files a/src/main/resources/assets/szar/icon.png and b/src/main/resources/assets/szar/icon.png differ diff --git a/src/main/resources/assets/szar/lang/en_us.json b/src/main/resources/assets/szar/lang/en_us.json index fb38574..603e385 100644 --- a/src/main/resources/assets/szar/lang/en_us.json +++ b/src/main/resources/assets/szar/lang/en_us.json @@ -88,5 +88,29 @@ "item.szar.hello.desc": "Alex Savage - OMFG - Hello (Dark Remix)", "death.attack.plane_crash": "%1$s crashed their plane", - "death.attack.plane_crash.player": "%1$s was killed when %2$s crashed their plane" + "death.attack.plane_crash.player": "%1$s was killed when %2$s crashed their plane", + + "painting.szar.nyansniffer.title": "Nyan Sniffer", + "painting.szar.nyansniffer.author": "Unknown (Szar Mod)", + + "painting.szar.matrix.title": "Matrix", + "painting.szar.matrix.author": "Unknown (Szar Mod)", + + "painting.szar.frogs.title": "Frogs", + "painting.szar.frogs.author": "Minecraft Live (Szar Mod)", + + "painting.szar.bounce.title": "Bounce", + "painting.szar.bounce.author": "Unknown (Szar Mod)", + + "painting.szar.block_wave.title": "Block Wave", + "painting.szar.block_wave.author": "Unknown (Szar Mod)", + + "painting.szar.bloc.title": "Bloc", + "painting.szar.bloc.author": "Unknown (Szar Mod)", + + "painting.szar.axolotl.title": "Axolotl", + "painting.szar.axolotl.author": "Unknown (Szar Mod)", + + "painting.szar.chicken_jokey.title": "Chicken Jokey", + "painting.szar.chicken_jokey.author": "Unknown (Szar Mod)" } diff --git a/src/main/resources/assets/szar/szarmod.png b/src/main/resources/assets/szar/szarmod.png new file mode 100644 index 0000000..6c0f0db Binary files /dev/null and b/src/main/resources/assets/szar/szarmod.png differ diff --git a/src/main/resources/assets/szar/textures/block/questionmark.png b/src/main/resources/assets/szar/textures/block/questionmark.png new file mode 100644 index 0000000..f04250a Binary files /dev/null and b/src/main/resources/assets/szar/textures/block/questionmark.png differ diff --git a/src/main/resources/assets/szar/textures/entity/gypsy.png b/src/main/resources/assets/szar/textures/entity/gypsy.png index 8f9744a..7925dcc 100644 Binary files a/src/main/resources/assets/szar/textures/entity/gypsy.png and b/src/main/resources/assets/szar/textures/entity/gypsy.png differ diff --git a/src/main/resources/assets/szar/textures/item/questionmark.png b/src/main/resources/assets/szar/textures/item/questionmark.png new file mode 100644 index 0000000..f04250a Binary files /dev/null and b/src/main/resources/assets/szar/textures/item/questionmark.png differ diff --git a/src/main/resources/assets/szar/textures/questionmark.png b/src/main/resources/assets/szar/textures/questionmark.png new file mode 100644 index 0000000..f04250a Binary files /dev/null and b/src/main/resources/assets/szar/textures/questionmark.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 45305b8..b053c6c 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -19,6 +19,9 @@ ], "main": [ "dev.tggamesyt.szar.Szar" + ], + "modmenu": [ + "dev.tggamesyt.szar.client.ModMenuIntegration" ] }, "mixins": [ @@ -32,5 +35,9 @@ "fabricloader": ">=${loader_version}", "fabric": "*", "minecraft": "${minecraft_version}" + }, + "suggests": { + "modmenu": "*", + "cloth-config": "*" } } diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/lang/en_us.json b/src/main/resources/resourcepacks/nsfw/assets/szar/lang/en_us.json new file mode 100644 index 0000000..de695d7 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/lang/en_us.json @@ -0,0 +1,7 @@ +{ + "block.szar.fasz": "?", + "item.szar.epstein_files": "?", + "item.szar.cndm": "?", + "death.attack.fck": "%1$s was killed by %2$s", + "item.szar.white_liquid": "?" +} diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/block/fasz.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/block/fasz.json new file mode 100644 index 0000000..07281de --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/block/fasz.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "szar:block/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/cndm.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/cndm.json new file mode 100644 index 0000000..18b4be4 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/cndm.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/epstein_files.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/epstein_files.json new file mode 100644 index 0000000..5442346 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/epstein_files.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} \ No newline at end of file diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz.json new file mode 100644 index 0000000..18b4be4 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz_in_hand.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz_in_hand.json new file mode 100644 index 0000000..18b4be4 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/fasz_in_hand.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/white_liquid.json b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/white_liquid.json new file mode 100644 index 0000000..18b4be4 --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/assets/szar/models/item/white_liquid.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/nsfw/pack.mcmeta b/src/main/resources/resourcepacks/nsfw/pack.mcmeta new file mode 100644 index 0000000..6a2f61e --- /dev/null +++ b/src/main/resources/resourcepacks/nsfw/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 15, + "description": "Szar built-in resource pack" + } +} \ No newline at end of file diff --git a/src/main/resources/resourcepacks/racist/assets/szar/lang/en_us.json b/src/main/resources/resourcepacks/racist/assets/szar/lang/en_us.json new file mode 100644 index 0000000..4400582 --- /dev/null +++ b/src/main/resources/resourcepacks/racist/assets/szar/lang/en_us.json @@ -0,0 +1,25 @@ +{ + "block.szar.cigany": "? Block", + "item.szar.nwordpass": "?", + "entity.szar.nigger": "Monkey", + "item.szar.nigger_spawn_egg":"Monkey Spawn Egg", + "item.szar.gypsy_spawn_egg":"Burglar Spawn Egg", + + "item.szar.niggerite_ingot": "Monkey Ingot", + "block.szar.niggerite_block": "Monkey Block", + "item.szar.niggerite_sword": "Monkey Sword", + "item.szar.niggerite_axe": "Monkey Axe", + "item.szar.niggerite_pickaxe": "Monkey Pickaxe", + "item.szar.niggerite_shovel": "Monkey Shovel", + "item.szar.niggerite_hoe": "Monkey Hoe", + "item.szar.niggerite_chestplate": "Monkey Chestplate", + "item.szar.niggerite_leggings": "Monkey Leggings", + "item.szar.niggerite_boots": "Monkey Boots", + "item.szar.niggerite_helmet": "Monkey Helmet", + "entity.szar.islam_terrorist": "Creeper Player", + "item.szar.terrorist_spawn_egg": "Creeper Player Spawn Egg", + "entity.szar.gypsy": "Burglar", + "item.szar.epstein_files": "?", + "entity.szar.epstein": "Old Man", + "item.szar.epstein_spawn_egg": "Old Man Spawn Egg" +} diff --git a/src/main/resources/resourcepacks/racist/assets/szar/models/block/cigany.json b/src/main/resources/resourcepacks/racist/assets/szar/models/block/cigany.json new file mode 100644 index 0000000..07281de --- /dev/null +++ b/src/main/resources/resourcepacks/racist/assets/szar/models/block/cigany.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "szar:block/questionmark" + } +} diff --git a/src/main/resources/resourcepacks/racist/assets/szar/models/item/epstein_files.json b/src/main/resources/resourcepacks/racist/assets/szar/models/item/epstein_files.json new file mode 100644 index 0000000..5442346 --- /dev/null +++ b/src/main/resources/resourcepacks/racist/assets/szar/models/item/epstein_files.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} \ No newline at end of file diff --git a/src/main/resources/resourcepacks/racist/assets/szar/models/item/nwordpass.json b/src/main/resources/resourcepacks/racist/assets/szar/models/item/nwordpass.json new file mode 100644 index 0000000..5442346 --- /dev/null +++ b/src/main/resources/resourcepacks/racist/assets/szar/models/item/nwordpass.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "szar:item/questionmark" + } +} \ No newline at end of file diff --git a/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/epstein.png b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/epstein.png new file mode 100644 index 0000000..03bb36b Binary files /dev/null and b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/epstein.png differ diff --git a/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/gypsy.png b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/gypsy.png new file mode 100644 index 0000000..32449db Binary files /dev/null and b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/gypsy.png differ diff --git a/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/islam_terrorist.png b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/islam_terrorist.png new file mode 100644 index 0000000..d145d39 Binary files /dev/null and b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/islam_terrorist.png differ diff --git a/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/nigg.png b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/nigg.png new file mode 100644 index 0000000..4948c11 Binary files /dev/null and b/src/main/resources/resourcepacks/racist/assets/szar/textures/entity/nigg.png differ diff --git a/src/main/resources/resourcepacks/racist/pack.mcmeta b/src/main/resources/resourcepacks/racist/pack.mcmeta new file mode 100644 index 0000000..6a2f61e --- /dev/null +++ b/src/main/resources/resourcepacks/racist/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 15, + "description": "Szar built-in resource pack" + } +} \ No newline at end of file