diff --git a/README.md b/README.md index 3b29a46..52d99f9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ -# Mumble Walkie - -In development +# Mumble Walkie (in development) + +Mumble Walkie is a Minecraft Modification for Fabric that brings real-time voice communication to the game. This mod integrates with Mumble, a voice chat platform, allowing players to communicate via a virtual radio channel. + +### Key Features: +- **Walkie-Talkie Item:** A custom item that players can hold in their hands to communicate with others. +- **Real-time Voice Chat:** Integrates with Mumble to enable voice chat between players within the proximity of the radio signal. +- **Sound Effects:** Realistic radio sound effects such as static, tuning, and voice transmission when interacting with the radio. +- **Voice Proximity:** Volume and voice distance are adjusted based on the player's position in the game world. +- **Interference Sounds:** When there is a storm, rain, or snow, players will hear radio interference to add to the immersion. +- **Easy Setup:** Configure your Mumble server and connection settings with ease, allowing for a smooth and intuitive setup. + +### Installation +1. Download and install Fabric mod loader. +2. Download the Mumble Walkie mod and place it in the `mods` folder. +3. Configure the Mumble server through the in-game settings or edit the `mbradio_config.json` file in the `config` folder. + +### Usage +- Equip the Walkie-Talkie item and hold the configured key ("V" key is default) to start speaking. +- Adjust the Mumble server settings if needed from the in-game menu or through the `mbradio_config.json` file. + +### Compatibility +- Compatible with other Minecraft mods and supports integration with additional communication tools or systems. +- Designed to work seamlessly on both client and server-side. diff --git a/build.gradle b/build.gradle index a41f396..d67e44d 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,9 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + + modImplementation "net.java.dev.jna:jna:5.17.0" + } processResources { diff --git a/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkie.java b/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkie.java index 58f00a1..724f139 100644 --- a/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkie.java +++ b/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkie.java @@ -1,5 +1,7 @@ package com.andrewdotdev.mbwalkie; +import com.andrewdotdev.mbwalkie.commands.WalkieCommands; +import com.andrewdotdev.mbwalkie.config.ConfigManager; import com.andrewdotdev.mbwalkie.item.ModItems; import net.fabricmc.api.ModInitializer; import org.slf4j.Logger; @@ -11,6 +13,8 @@ public class MumbleWalkie implements ModInitializer { @Override public void onInitialize() { + ConfigManager.loadConfig(); + WalkieCommands.register(); ModItems.registerModItems(); LOGGER.info("Mumble Walkie initialized!"); } diff --git a/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkieClient.java b/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkieClient.java index 8c4fd22..00808d4 100644 --- a/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkieClient.java +++ b/src/main/java/com/andrewdotdev/mbwalkie/MumbleWalkieClient.java @@ -1,10 +1,13 @@ package com.andrewdotdev.mbwalkie; +import com.andrewdotdev.mbwalkie.client.ClientEvents; +import com.andrewdotdev.mbwalkie.client.Keybinds; import net.fabricmc.api.ClientModInitializer; public class MumbleWalkieClient implements ClientModInitializer { @Override public void onInitializeClient() { - + Keybinds.register(); + ClientEvents.register(); } } diff --git a/src/main/java/com/andrewdotdev/mbwalkie/client/ClientEvents.java b/src/main/java/com/andrewdotdev/mbwalkie/client/ClientEvents.java new file mode 100644 index 0000000..cd7fb3f --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/client/ClientEvents.java @@ -0,0 +1,71 @@ +package com.andrewdotdev.mbwalkie.client; + +import com.andrewdotdev.mbwalkie.mumble.MumbleClient; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registry; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; + +import java.util.Optional; + +import static com.andrewdotdev.mbwalkie.item.ModItems.WALKIE_TALKIE; + +public class ClientEvents { + private static boolean isTalking = false; + + public static void register() { + + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> MumbleClient.connect()); + + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (hasWalkieTalkie(client)) { + if (Keybinds.radioKey.isPressed() && !isTalking) { + startTalking(client); + isTalking = true; + } else if (!Keybinds.radioKey.isPressed() && isTalking) { + stopTalking(client); + isTalking = false; + } + } else if (!hasWalkieTalkie(client) && isTalking) { + stopTalking(client); + isTalking = false; + } + }); + } + + private static boolean hasWalkieTalkie(MinecraftClient client) { + if (client.player == null) return false; + + ItemStack mainHand = client.player.getMainHandStack(); + ItemStack offHand = client.player.getOffHandStack(); + + return mainHand.isOf(WALKIE_TALKIE) || offHand.isOf(WALKIE_TALKIE); + } + + private static void startTalking(MinecraftClient client) { + playSound(client, "mbwalkie:walkie_start"); + MumbleClient.startTalking(); + System.out.println("🎙️ Started Talking..."); + } + + private static void stopTalking(MinecraftClient client) { + playSound(client, "mbwalkie:walkie_end"); + MumbleClient.stopTalking(); + System.out.println("📻 Stopped Talking."); + } + + private static void playSound(MinecraftClient client, String soundId) { + if (client.player == null) return; + + SoundEvent soundEvent = new SoundEvent(Identifier.of(soundId), Optional.of(8F)); + if (soundEvent != null) { + client.getSoundManager().play( + PositionedSoundInstance.master(soundEvent, 1.0F) + ); + } + } +} diff --git a/src/main/java/com/andrewdotdev/mbwalkie/client/Keybinds.java b/src/main/java/com/andrewdotdev/mbwalkie/client/Keybinds.java new file mode 100644 index 0000000..b33e05a --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/client/Keybinds.java @@ -0,0 +1,19 @@ +package com.andrewdotdev.mbwalkie.client; + +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import org.lwjgl.glfw.GLFW; + +public class Keybinds { + public static KeyBinding radioKey; + + public static void register() { + radioKey = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "Talk", // Nombre en el archivo de traducción + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_V, // Tecla por defecto (puede cambiarse en ajustes) + "Mumble Walkie Talkie" // Categoría en los controles + )); + } +} diff --git a/src/main/java/com/andrewdotdev/mbwalkie/commands/WalkieCommands.java b/src/main/java/com/andrewdotdev/mbwalkie/commands/WalkieCommands.java new file mode 100644 index 0000000..9e6cdf8 --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/commands/WalkieCommands.java @@ -0,0 +1,36 @@ +package com.andrewdotdev.mbwalkie.commands; + +import com.andrewdotdev.mbwalkie.config.ConfigManager; +import com.andrewdotdev.mbwalkie.mumble.MumbleClient; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; + +import java.util.function.Supplier; + +public class WalkieCommands { + public static void register() { + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { + dispatcher.register(CommandManager.literal("reconnect") + .executes(context -> { + reconnectToMumble(context.getSource()); + return Command.SINGLE_SUCCESS; + }) + ); + }); + } + + private static void reconnectToMumble(ServerCommandSource source) { + source.sendFeedback(() -> Text.literal("🔄 Reconectando a Mumble..."), false); + + // Desconectar y recargar la configuración + MumbleClient.disconnect(); + ConfigManager.loadConfig(); + MumbleClient.connect(); + + source.sendFeedback(() -> Text.literal("✅ Reconectado a " + ConfigManager.getMumbleIP() + ":" + ConfigManager.getMumblePort()), false); + } +} diff --git a/src/main/java/com/andrewdotdev/mbwalkie/config/ConfigManager.java b/src/main/java/com/andrewdotdev/mbwalkie/config/ConfigManager.java new file mode 100644 index 0000000..6cc9e07 --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/config/ConfigManager.java @@ -0,0 +1,47 @@ +package com.andrewdotdev.mbwalkie.config; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public class ConfigManager { + private static final File CONFIG_FILE = new File("config/mbwalkie_config.json"); + private static JsonObject configData; + private static final Gson GSON = new Gson(); + + public static void loadConfig() { + if (!CONFIG_FILE.exists()) { + saveDefaultConfig(); + } + + try (FileReader reader = new FileReader(CONFIG_FILE)) { + configData = GSON.fromJson(reader, JsonObject.class); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void saveDefaultConfig() { + JsonObject defaultConfig = new JsonObject(); + defaultConfig.addProperty("mumble_ip", "127.0.0.1"); + defaultConfig.addProperty("mumble_port", 64738); + + try (FileWriter writer = new FileWriter(CONFIG_FILE)) { + GSON.toJson(defaultConfig, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String getMumbleIP() { + return configData.get("mumble_ip").getAsString(); + } + + public static int getMumblePort() { + return configData.get("mumble_port").getAsInt(); + } +} diff --git a/src/main/java/com/andrewdotdev/mbwalkie/config/mbwalkie_config.json b/src/main/java/com/andrewdotdev/mbwalkie/config/mbwalkie_config.json new file mode 100644 index 0000000..069d57d --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/config/mbwalkie_config.json @@ -0,0 +1,4 @@ +{ + "mumble_ip": "127.0.0.1", + "mumble_port": 64738 +} \ No newline at end of file diff --git a/src/main/java/com/andrewdotdev/mbwalkie/mumble/MumbleClient.java b/src/main/java/com/andrewdotdev/mbwalkie/mumble/MumbleClient.java new file mode 100644 index 0000000..7d21c18 --- /dev/null +++ b/src/main/java/com/andrewdotdev/mbwalkie/mumble/MumbleClient.java @@ -0,0 +1,89 @@ +package com.andrewdotdev.mbwalkie.mumble; + +import com.andrewdotdev.mbwalkie.config.ConfigManager; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; + +import java.io.IOException; +import java.net.Socket; +import java.util.Optional; + +public class MumbleClient { + private static Socket socket; + private static boolean connected = false; + private static final double MAX_RANGE = 50.0; + private static boolean transmitting = false; + + public static void startTalking() { + transmitting = true; + updateVolume(); + System.out.println("🎙 Transmitiendo voz..."); + } + + public static void stopTalking() { + transmitting = false; + System.out.println("🔇 Voz detenida."); + } + + private static void updateVolume() { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null) return; + + assert client.world != null; + boolean isStormy = client.world.isThundering() || client.world.isRaining(); + + for (PlayerEntity otherPlayer : client.world.getPlayers()) { + if (otherPlayer == client.player) continue; + + double distance = client.player.getPos().distanceTo(otherPlayer.getPos()); + float volume = calculateVolume(distance); + + if (isStormy || distance > 30) { + playInterferenceSound(); + } + + System.out.println("🔊 Ajustando volumen para " + otherPlayer.getName().getString() + ": " + volume); + } + } + + private static void playInterferenceSound() { + MinecraftClient client = MinecraftClient.getInstance(); + assert client.player != null; + client.player.playSound(new SoundEvent(Identifier.of("mbwalkie:walkie_static"), Optional.of(8F)), 1.0f, 1.0f); + } + + private static float calculateVolume(double distance) { + if (distance > MAX_RANGE) return 0.0f; + return 1.0f - (float) (distance / MAX_RANGE); + } + + + public static void connect() { + if (connected) return; + + try { + String ip = ConfigManager.getMumbleIP(); + int port = ConfigManager.getMumblePort(); + + socket = new Socket(ip, port); + connected = true; + System.out.println("✅ Connected in " + ip + ":" + port); + } catch (IOException e) { + System.err.println("❌ Error while trying to connect Mumble: " + e.getMessage()); + } + } + + public static void disconnect() { + if (!connected) return; + + try { + socket.close(); + connected = false; + System.out.println("🔴 Mumble Disconnected"); + } catch (IOException e) { + System.err.println("⚠️ Error while trying to disconnect Mumble: " + e.getMessage()); + } + } +} diff --git a/src/main/resources/assets/mbwalkie/models/item/walkie_talkie.json b/src/main/resources/assets/mbwalkie/models/item/walkie_talkie.json index b90e27c..d59d170 100644 --- a/src/main/resources/assets/mbwalkie/models/item/walkie_talkie.json +++ b/src/main/resources/assets/mbwalkie/models/item/walkie_talkie.json @@ -1,5 +1,5 @@ { - "credit": "Made with Blockbench", + "credit": "Made by @andrewdotdev", "texture_size": [64, 64], "textures": { "0": "mbwalkie:item/walkie_talkie", diff --git a/src/main/resources/assets/mbwalkie/sounds.json b/src/main/resources/assets/mbwalkie/sounds.json new file mode 100644 index 0000000..7768262 --- /dev/null +++ b/src/main/resources/assets/mbwalkie/sounds.json @@ -0,0 +1,11 @@ +{ + "walkie_start": { + "sounds": ["mbwalkie:walkie_start"] + }, + "walkie_end": { + "sounds": ["mbwalkie:walkie_end"] + }, + "walkie_static": { + "sounds": ["mbwalkie:walkie_static"] + } +} diff --git a/src/main/resources/assets/mbwalkie/sounds/walkie_end.ogg b/src/main/resources/assets/mbwalkie/sounds/walkie_end.ogg new file mode 100644 index 0000000..932c874 Binary files /dev/null and b/src/main/resources/assets/mbwalkie/sounds/walkie_end.ogg differ diff --git a/src/main/resources/assets/mbwalkie/sounds/walkie_start.ogg b/src/main/resources/assets/mbwalkie/sounds/walkie_start.ogg new file mode 100644 index 0000000..1996e7b Binary files /dev/null and b/src/main/resources/assets/mbwalkie/sounds/walkie_start.ogg differ diff --git a/src/main/resources/assets/mbwalkie/sounds/walkie_static.ogg b/src/main/resources/assets/mbwalkie/sounds/walkie_static.ogg new file mode 100644 index 0000000..c4126d9 Binary files /dev/null and b/src/main/resources/assets/mbwalkie/sounds/walkie_static.ogg differ