19 Commits

Author SHA1 Message Date
ba6153fa41 Updated README.md
All checks were successful
build / build (push) Successful in 1m15s
2025-04-12 17:29:40 +02:00
798027a3af Updated mod version 2025-04-12 17:19:40 +02:00
6b2d080746 Made teleport head lock toggleable 2025-04-12 16:59:39 +02:00
87b6900ff3 Cleaned up 2025-04-12 16:59:27 +02:00
c39249e3c1 Optimized chat message system 2025-04-12 16:31:45 +02:00
281297bdca Cleaned up
All checks were successful
build / build (push) Successful in 1m13s
2025-04-12 16:21:27 +02:00
55e9b0fafd Added /camera keybind 2025-04-12 16:19:59 +02:00
4b5b697883 Added color to Chat Calc answer & made optional to add an equals sign 2025-04-12 16:19:36 +02:00
9325e24c5c Removed unnecessary comment
All checks were successful
build / build (push) Successful in 1m16s
2025-04-12 15:21:21 +02:00
02d2f624cf Code cleanup 2025-04-11 17:30:08 +02:00
9041bacf48 Updated README.md 2025-04-11 17:21:59 +02:00
23aefb2f8d Code cleanup 2025-04-11 17:21:51 +02:00
0941d3929e Renamed Utils.java to PacketUtils.java 2025-04-11 14:18:38 +02:00
c7722115ed Fixed bug where EC closes if another player is still inside 2025-04-11 14:17:14 +02:00
168f916baa Moved features to "features" package 2025-04-11 14:07:19 +02:00
f28159b442 Open/close EC block while opening/closing SEC + Play open & close sounds 2025-04-11 14:06:21 +02:00
c3a13c8063 Optimized fog removal ever so slightly 2025-04-11 13:21:46 +02:00
6b806574ac Removed Darkness Effect (toggleable)
All checks were successful
build / build (push) Successful in 1m19s
2025-04-11 13:17:19 +02:00
ddd5b9bf75 Fixed typo
All checks were successful
build / build (push) Successful in 1m12s
2025-04-07 10:37:44 +02:00
35 changed files with 380 additions and 300 deletions

View File

@ -3,7 +3,7 @@
# ![Logo](https://i.imgur.com/ktkHND1.png) # ![Logo](https://i.imgur.com/ktkHND1.png)
This mod is FAR FROM FINISHED, and initially just created for our private Survival Server. This mod is FAR FROM FINISHED, and initially just created for our private Survival Server.
As a challenge I'm trying to make it as user-friendly as possible. (It ain't there yet tho ;) ) As a challenge I'm trying to make it as user-friendly as possible.
# Current feature-set # Current feature-set
## Server Side ## Server Side
@ -26,26 +26,35 @@ As a challenge I'm trying to make it as user-friendly as possible. (It ain't the
- Version "control" - Version "control"
- Shared Ender Chest - Shared Ender Chest
- Shared EC Access control (via config) - Shared EC Access control (via config)
- Open/close EC block while opening/closing SEC
- Play open & close sounds
- Vein miner - Vein miner
![VeinMiner](https://i.imgur.com/zOXWMNa.gif) ![VeinMiner](https://i.imgur.com/zOXWMNa.gif)
- Chat Calculator
### Commands ### Commands
- /spectator | Essentially server-side free-cam, you get put in spectator and are able to fly around, once you use the command again you get put back to where you were. - /spectator | Essentially server-side free-cam, you get put in spectator and are able to fly around, once you use the command again you get put back to where you were.
- /slimechunk (/sc) | See if you're currently in a slimechunk - /slimechunk (/sc) | See if you're currently in a slimechunk
# Currently working on 1.3.2
## Server Side
- [x] Chat Calculator
## Client Side ## Client Side
- [x] Teleportation Angle Viewer for [this machine](https://www.youtube.com/watch?v=FnUE-ZaALLw)
![Teleportation Keybindings](https://i.imgur.com/gjO1H3d.png)
- [x] Removed game fog (lava, water, etc.)
- [ ] Made it toggleable
- [ ] Mod Menu integration
# Features to come - Teleportation Angle Viewer for [this machine](https://www.youtube.com/watch?v=FnUE-ZaALLw)
- Toggleable pitch/yaw lock while teleporting
![Teleportation Keybindings](https://i.imgur.com/gjO1H3d.png)
- Remove game fog (lava, water, etc.)
- All types individually toggleable
- Mod Menu integration
- Automatic config adaption (currently booleans only)
- Remove darkness effect
- Toggleable
- Keybinding for /camera
# To-do
## General
- Rework config system
- Store server settings in world folder for better singleplayer use
- Rework Mod Menu integration to be more flexible
## Server Side ## Server Side
- Telekinesis - Telekinesis

View File

@ -9,7 +9,7 @@ yarn_mappings=1.21.5+build.1
loader_version=0.16.10 loader_version=0.16.10
# Mod Properties # Mod Properties
mod_version=1.3.2 mod_version=1.4.0
maven_group=wtf.hak.survivalfabric maven_group=wtf.hak.survivalfabric
archives_base_name=survivalfabric archives_base_name=survivalfabric

View File

@ -3,12 +3,21 @@ package wtf.hak.survivalfabric;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import wtf.hak.survivalfabric.config.client.ClientConfigManager; import wtf.hak.survivalfabric.config.client.ClientConfigManager;
import wtf.hak.survivalfabric.features.AngleViewer; import wtf.hak.survivalfabric.features.AngleViewer;
import wtf.hak.survivalfabric.features.RemoveDarknessEffect;
import wtf.hak.survivalfabric.features.SFKeyBindings;
public class SurvivalFabricClient implements ClientModInitializer { public class SurvivalFabricClient implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
ClientConfigManager.getConfig();
AngleViewer.register(); // Config
} ClientConfigManager.getConfig();
// Features
AngleViewer.register();
RemoveDarknessEffect.register();
SFKeyBindings.register();
}
} }

View File

@ -4,8 +4,8 @@ import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
public class SurvivalFabricDataGenerator implements DataGeneratorEntrypoint { public class SurvivalFabricDataGenerator implements DataGeneratorEntrypoint {
@Override @Override
public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
} }
} }

View File

@ -9,4 +9,6 @@ public class ClientConfig {
public boolean renderLavaFog = false; public boolean renderLavaFog = false;
public boolean renderWaterFog = false; public boolean renderWaterFog = false;
public boolean renderSnowFog = false; public boolean renderSnowFog = false;
public boolean removeDarknessEffect = true;
public boolean lockTeleportHeadMovement = true;
} }

View File

@ -25,7 +25,7 @@ public class ClientConfigManager {
} }
public static ClientConfig load() { public static ClientConfig load() {
try(FileReader reader = new FileReader(CONFIG_FILE)) { try (FileReader reader = new FileReader(CONFIG_FILE)) {
INSTANCE = GSON.fromJson(reader, ClientConfig.class); INSTANCE = GSON.fromJson(reader, ClientConfig.class);
if (INSTANCE.configVersion.equalsIgnoreCase(new Config().configVersion)) { if (INSTANCE.configVersion.equalsIgnoreCase(new Config().configVersion)) {
return INSTANCE; return INSTANCE;
@ -41,12 +41,12 @@ public class ClientConfigManager {
} }
} }
public static void save(){ public static void save() {
save(INSTANCE); save(INSTANCE);
} }
public static void save(ClientConfig config) { public static void save(ClientConfig config) {
try(FileWriter writer = new FileWriter(CONFIG_FILE)) { try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(config, writer); GSON.toJson(config, writer);
} catch (IOException e) { } catch (IOException e) {
System.out.println("Error saving config: " + e.getMessage()); System.out.println("Error saving config: " + e.getMessage());

View File

@ -17,7 +17,7 @@ public class AngleViewer {
public static boolean PREVENT_HEAD_MOVEMENT = false; public static boolean PREVENT_HEAD_MOVEMENT = false;
public static void register() { public static void register() {
for(Angle angle : Angle.values()) { for (Angle angle : Angle.values()) {
KeyBinding keyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding( KeyBinding keyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.survivalfabric." + angle.name().toLowerCase(), "key.survivalfabric." + angle.name().toLowerCase(),
InputUtil.Type.KEYSYM, InputUtil.Type.KEYSYM,
@ -28,14 +28,14 @@ public class AngleViewer {
ClientTickEvents.END_CLIENT_TICK.register(mc -> { ClientTickEvents.END_CLIENT_TICK.register(mc -> {
while (keyBinding.wasPressed()) { while (keyBinding.wasPressed()) {
ClientPlayerEntity player = mc.player; ClientPlayerEntity player = mc.player;
if(player == null) return; if (player == null) return;
player.setYaw(angle.yaw); player.setYaw(angle.yaw);
player.setPitch(angle.pitch); player.setPitch(angle.pitch);
PREVENT_HEAD_MOVEMENT = true; PREVENT_HEAD_MOVEMENT = true;
scheduler.schedule(() -> { scheduler.schedule(() -> {
if(player == null) return; if (player == null) return;
PREVENT_HEAD_MOVEMENT = false; PREVENT_HEAD_MOVEMENT = false;
player.setPitch(-90); player.setPitch(-90);
PREVENT_HEAD_MOVEMENT = true; PREVENT_HEAD_MOVEMENT = true;

View File

@ -0,0 +1,19 @@
package wtf.hak.survivalfabric.features;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
public class RemoveDarknessEffect {
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (client.player != null && ClientConfigManager.getConfig().removeDarknessEffect) {
StatusEffectInstance darknessEffect = client.player.getStatusEffect(StatusEffects.DARKNESS);
if (darknessEffect != null)
client.player.removeStatusEffect(darknessEffect.getEffectType());
}
});
}
}

View File

@ -0,0 +1,22 @@
package wtf.hak.survivalfabric.features;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
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 SFKeyBindings {
private static final KeyBinding CAMERA_BIND = KeyBindingHelper.registerKeyBinding(new KeyBinding("key.survivalfabric.camera", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_PERIOD, "category.survivalfabric.survivalfabric"));
public static void register() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if(client.player != null) {
if (CAMERA_BIND.wasPressed()) {
client.player.networkHandler.sendChatCommand("camera");
}
}
});
}
}

View File

@ -8,6 +8,7 @@ import net.minecraft.client.render.FogShape;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.joml.Vector4f; import org.joml.Vector4f;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@ -17,26 +18,30 @@ import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig
@Mixin(value = BackgroundRenderer.class, priority = 910) @Mixin(value = BackgroundRenderer.class, priority = 910)
public abstract class BackgroundRendererMixin { public abstract class BackgroundRendererMixin {
@Unique
private static final Fog EMPTY_FOG = new Fog(-8.0f, 1_000_000.0F, FogShape.CYLINDER, 0, 0, 0, 0);
@Inject(method = "applyFog", at = @At("RETURN"), cancellable = true) @Inject(method = "applyFog", at = @At("RETURN"), cancellable = true)
private static void applyFog(Camera camera, BackgroundRenderer.FogType fogType, Vector4f color, float viewDistance, boolean thickenFog, float tickProgress, CallbackInfoReturnable<Fog> cir) { private static void applyFog(Camera camera, BackgroundRenderer.FogType fogType, Vector4f color, float viewDistance, boolean thickenFog, float tickProgress, CallbackInfoReturnable<Fog> cir) {
CameraSubmersionType submersion = camera.getSubmersionType();
boolean renderFog = true; boolean renderFog = true;
CameraSubmersionType subType = camera.getSubmersionType();
if(subType == CameraSubmersionType.NONE) {
World world = camera.getFocusedEntity().getWorld();
if(world.getRegistryKey() == World.OVERWORLD && !getConfig().renderOverworldFog)
renderFog = false;
else if(world.getRegistryKey() == World.NETHER && !getConfig().renderNetherFog)
renderFog = false;
else if(world.getRegistryKey() == World.END && !getConfig().renderEndFog)
renderFog = false;
} else if(subType == CameraSubmersionType.WATER && !getConfig().renderWaterFog)
renderFog = false;
else if(subType == CameraSubmersionType.LAVA && !getConfig().renderLavaFog)
renderFog = false;
else if(subType == CameraSubmersionType.POWDER_SNOW && !getConfig().renderSnowFog)
renderFog = false;
if(!renderFog) switch (submersion) {
cir.setReturnValue(new Fog(-8.0f, 1_000_000.0F, FogShape.CYLINDER, 0,0,0,0)); case NONE -> {
World world = camera.getFocusedEntity().getWorld();
if ((world.getRegistryKey() == World.OVERWORLD && !getConfig().renderOverworldFog)
|| (world.getRegistryKey() == World.NETHER && !getConfig().renderNetherFog)
|| (world.getRegistryKey() == World.END && !getConfig().renderEndFog)) {
renderFog = false;
}
}
case WATER -> renderFog = getConfig().renderWaterFog;
case LAVA -> renderFog = getConfig().renderLavaFog;
case POWDER_SNOW -> renderFog = getConfig().renderSnowFog;
}
if (!renderFog) {
cir.setReturnValue(EMPTY_FOG);
}
} }
} }

View File

@ -6,6 +6,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
import wtf.hak.survivalfabric.features.AngleViewer; import wtf.hak.survivalfabric.features.AngleViewer;
@Mixin(Entity.class) @Mixin(Entity.class)
@ -13,8 +14,8 @@ public abstract class EntityMixin {
@Inject(method = "setYaw", at = @At("HEAD"), cancellable = true) @Inject(method = "setYaw", at = @At("HEAD"), cancellable = true)
private void preventYawChange(float yaw, CallbackInfo ci) { private void preventYawChange(float yaw, CallbackInfo ci) {
if((Object) this instanceof ClientPlayerEntity player) { if ((Object) this instanceof ClientPlayerEntity player) {
if(player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT) { if (player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT && ClientConfigManager.getConfig().lockTeleportHeadMovement) {
ci.cancel(); ci.cancel();
} }
} }
@ -22,8 +23,8 @@ public abstract class EntityMixin {
@Inject(method = "setPitch", at = @At("HEAD"), cancellable = true) @Inject(method = "setPitch", at = @At("HEAD"), cancellable = true)
private void preventPitchChange(float pitch, CallbackInfo ci) { private void preventPitchChange(float pitch, CallbackInfo ci) {
if((Object) this instanceof ClientPlayerEntity player) { if ((Object) this instanceof ClientPlayerEntity player) {
if(player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT) { if (player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT && ClientConfigManager.getConfig().lockTeleportHeadMovement) {
ci.cancel(); ci.cancel();
} }
} }

View File

@ -55,7 +55,6 @@ public class ConfigScreen extends Screen {
} }
} }
// Done button
this.addDrawableChild(ButtonWidget.builder( this.addDrawableChild(ButtonWidget.builder(
Text.translatable("gui.done"), Text.translatable("gui.done"),
button -> this.client.setScreen(parent) button -> this.client.setScreen(parent)

View File

@ -10,7 +10,6 @@ public class ModMenuIntegration implements ModMenuApi {
@Override @Override
public ConfigScreenFactory<?> getModConfigScreenFactory() { public ConfigScreenFactory<?> getModConfigScreenFactory() {
System.out.println("Does ModMenuIntegration even load?"); return ConfigScreen::new;
return parent -> new ConfigScreen(parent);
} }
} }

View File

@ -8,5 +8,5 @@
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
} }

View File

@ -1,7 +1,6 @@
package wtf.hak.survivalfabric; package wtf.hak.survivalfabric;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
@ -11,37 +10,36 @@ import org.slf4j.LoggerFactory;
import wtf.hak.survivalfabric.commands.ReloadConfigCommand; import wtf.hak.survivalfabric.commands.ReloadConfigCommand;
import wtf.hak.survivalfabric.commands.SlimeChunkCommand; import wtf.hak.survivalfabric.commands.SlimeChunkCommand;
import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand;
import wtf.hak.survivalfabric.sharedenderchest.SharedEnderChest; import wtf.hak.survivalfabric.features.sharedenderchest.SharedEnderChest;
import wtf.hak.survivalfabric.veinminer.VeinMinerEvents; import wtf.hak.survivalfabric.features.veinminer.VeinMinerEvents;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class SurvivalFabric implements ModInitializer { public class SurvivalFabric implements ModInitializer {
public static final String MOD_ID = "survivalfabric"; public static final String MOD_ID = "survivalfabric";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override @Override
public void onInitialize() { public void onInitialize() {
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, new String[] { "spectator", "s", "S", "camera", "c", "C", })); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, "spectator", "s", "S", "camera", "c", "C"));
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, new String[] { "reloadsurvivalconfig" })); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, "reloadsurvivalconfig"));
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SlimeChunkCommand.register(dispatcher, new String[] { "slimechunk", "sc" })); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SlimeChunkCommand.register(dispatcher, "slimechunk", "sc"));
if(getConfig().sharedEnderChestEnabled) if (getConfig().sharedEnderChestEnabled)
new SharedEnderChest().onInitialize(); new SharedEnderChest().onInitialize();
if(getConfig().veinMinerEnabled) { if (getConfig().veinMinerEnabled) {
PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> { PlayerBlockBreakEvents.BEFORE.register((world, player, pos, state, blockEntity) -> {
if (player instanceof ServerPlayerEntity serverPlayer) { if (player instanceof ServerPlayerEntity serverPlayer) {
return VeinMinerEvents.beforeBlockBreak(world, serverPlayer, pos, state); return VeinMinerEvents.beforeBlockBreak(world, serverPlayer, pos, state);
} } else {
else { return true;
return true; }
} });
});
ServerEntityEvents.ENTITY_LOAD.register(VeinMinerEvents::onEntityLoad); ServerEntityEvents.ENTITY_LOAD.register(VeinMinerEvents::onEntityLoad);
} }
} }
} }

View File

@ -6,11 +6,9 @@ import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.random.CheckedRandom;
import net.minecraft.util.math.random.ChunkRandom; import net.minecraft.util.math.random.ChunkRandom;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import wtf.hak.survivalfabric.config.ConfigManager;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
@ -31,7 +29,7 @@ public class SlimeChunkCommand {
ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity(); ServerPlayerEntity p = (ServerPlayerEntity) source.getEntity();
Chunk chunk = p.getServerWorld().getChunk(p.getBlockPos()); Chunk chunk = p.getServerWorld().getChunk(p.getBlockPos());
Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getServerWorld().getSeed(), 987234911L); Random slimeRandom = ChunkRandom.getSlimeRandom(chunk.getPos().x, chunk.getPos().z, p.getServerWorld().getSeed(), 987234911L);
if(slimeRandom.nextInt(10) == 0) { if (slimeRandom.nextInt(10) == 0) {
p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage)); p.sendMessage(Text.literal(getConfig().inSlimeChunkMessage));
} else } else
p.sendMessage(Text.literal(getConfig().notInSlimeChunkMessage)); p.sendMessage(Text.literal(getConfig().notInSlimeChunkMessage));

View File

@ -8,7 +8,7 @@ import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.world.GameMode; import net.minecraft.world.GameMode;
import wtf.hak.survivalfabric.utils.Utils; import wtf.hak.survivalfabric.utils.PacketUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -36,7 +36,7 @@ public class SpectatorCommand {
player.teleport(data.world, data.x, data.y, data.z, Set.of(), data.yaw, data.pitch, false); player.teleport(data.world, data.x, data.y, data.z, Set.of(), data.yaw, data.pitch, false);
player.changeGameMode(GameMode.SURVIVAL); player.changeGameMode(GameMode.SURVIVAL);
spectating.remove(player); spectating.remove(player);
Utils.updateListNames(player); PacketUtils.updateListNames(player);
} else { } else {
spectating.put(player, new LocationData(player spectating.put(player, new LocationData(player
@ -47,7 +47,7 @@ public class SpectatorCommand {
.getPitch(), player .getPitch(), player
.getServerWorld())); .getServerWorld()));
player.changeGameMode(GameMode.SPECTATOR); player.changeGameMode(GameMode.SPECTATOR);
Utils.updateListNames(player); PacketUtils.updateListNames(player);
} }
return 1; return 1;
} }

View File

@ -24,7 +24,7 @@ public class ConfigManager {
} }
public static Config load() { public static Config load() {
try(FileReader reader = new FileReader(CONFIG_FILE)) { try (FileReader reader = new FileReader(CONFIG_FILE)) {
INSTANCE = GSON.fromJson(reader, Config.class); INSTANCE = GSON.fromJson(reader, Config.class);
if (INSTANCE.configVersion.equalsIgnoreCase(new Config().configVersion)) { if (INSTANCE.configVersion.equalsIgnoreCase(new Config().configVersion)) {
return INSTANCE; return INSTANCE;
@ -41,7 +41,7 @@ public class ConfigManager {
} }
public static void save(Config config) { public static void save(Config config) {
try(FileWriter writer = new FileWriter(CONFIG_FILE)) { try (FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(config, writer); GSON.toJson(config, writer);
} catch (IOException e) { } catch (IOException e) {
System.out.println("Error saving config: " + e.getMessage()); System.out.println("Error saving config: " + e.getMessage());

View File

@ -1,15 +1,9 @@
package wtf.hak.survivalfabric.sharedenderchest; package wtf.hak.survivalfabric.features.sharedenderchest;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.block.Blocks;
import net.minecraft.block.EnderChestBlock; import net.minecraft.block.EnderChestBlock;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventories; import net.minecraft.inventory.Inventories;
@ -23,11 +17,14 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.*; import net.minecraft.util.ActionResult;
import net.minecraft.util.WorldSavePath;
import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.io.*;
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
@ -37,6 +34,51 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
private long ticksUntilSave = -20; private long ticksUntilSave = -20;
public static void saveInventory(MinecraftServer server) {
File inventoryFile = getFile(server);
NbtCompound nbt = new NbtCompound();
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
Inventories.writeNbt(nbt, sharedInventory.getList(inventoryItemStacks), server.getRegistryManager());
try (FileOutputStream inventoryFileOutputStream = new FileOutputStream(inventoryFile);
DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) {
inventoryFile.createNewFile();
NbtIo.writeCompressed(nbt, inventoryFileDataOutput);
} catch (Exception e) {
LOGGER.error("Error while saving Shared Ender Chest: " + e);
}
}
public static void openSharedEnderChest(PlayerEntity player, World world, BlockPos pos) {
fakeEnderChestOpen(world, pos, true);
sharedInventory.openedEnderChests.put(player, pos);
player.openHandledScreen(new SimpleNamedScreenHandlerFactory((int_1, playerInventory, playerEntity) ->
new GenericContainerScreenHandler(getConfig().screenHandlerType(), int_1, playerInventory, sharedInventory, getConfig().sharedEnderChestRows), Text.of(getConfig().sharedEnderChestName)));
}
public static void playEnderChestOpenSound(World world, BlockPos pos) {
world.playSound(null, pos, SoundEvents.BLOCK_ENDER_CHEST_OPEN, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F);
}
public static void playEnderChestCloseSound(World world, BlockPos pos) {
world.playSound(null, pos, SoundEvents.BLOCK_ENDER_CHEST_CLOSE, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F);
}
public static void fakeEnderChestOpen(World world, BlockPos pos, boolean open) {
if (!(world.getBlockState(pos).getBlock() instanceof EnderChestBlock)) {
return;
}
if (open)
playEnderChestOpenSound(world, pos);
else
playEnderChestCloseSound(world, pos);
world.addSyncedBlockEvent(pos, Blocks.ENDER_CHEST, 1, open ? 1 : 0);
}
private static File getFile(MinecraftServer server) {
return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile();
}
public void onServerStarted(MinecraftServer server) { public void onServerStarted(MinecraftServer server) {
File inventoryFile = getFile(server); File inventoryFile = getFile(server);
if (inventoryFile.exists()) { if (inventoryFile.exists()) {
@ -55,19 +97,6 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
} }
} }
public static void saveInventory(MinecraftServer server) {
File inventoryFile = getFile(server);
NbtCompound nbt = new NbtCompound();
DefaultedList<ItemStack> inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY);
Inventories.writeNbt(nbt, sharedInventory.getList(inventoryItemStacks), server.getRegistryManager());
try (FileOutputStream inventoryFileOutputStream = new FileOutputStream(inventoryFile);
DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) {
inventoryFile.createNewFile();
NbtIo.writeCompressed(nbt, inventoryFileDataOutput);
} catch (Exception e) {
LOGGER.error("Error while saving Shared Ender Chest: " + e);
}
}
public void onServerStopping(MinecraftServer server) { public void onServerStopping(MinecraftServer server) {
saveInventory(server); saveInventory(server);
} }
@ -86,17 +115,15 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
if (world.getBlockState(hitResult.getBlockPos()).getBlock() instanceof EnderChestBlock) { if (world.getBlockState(hitResult.getBlockPos()).getBlock() instanceof EnderChestBlock) {
if (!player.isSpectator()) { if (!player.isSpectator()) {
if(!getConfig().sharedEnderChestLimitedAccess) { if (!getConfig().sharedEnderChestLimitedAccess) {
if (world.isClient()) return ActionResult.SUCCESS; if (world.isClient()) return ActionResult.SUCCESS;
playEnderChestOpenSound(world, hitResult.getBlockPos()); openSharedEnderChest(player, world, hitResult.getBlockPos());
openSharedEnderChest(player);
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} else { } else {
for(String name : getConfig().sharedEnderChestNames) { for (String name : getConfig().sharedEnderChestNames) {
if(name.toLowerCase().strip().equalsIgnoreCase(player.getNameForScoreboard().toLowerCase())) { if (name.toLowerCase().strip().equalsIgnoreCase(player.getNameForScoreboard().toLowerCase())) {
if (world.isClient()) return ActionResult.SUCCESS; if (world.isClient()) return ActionResult.SUCCESS;
playEnderChestOpenSound(world, hitResult.getBlockPos()); openSharedEnderChest(player, world, hitResult.getBlockPos());
openSharedEnderChest(player);
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
} }
@ -113,17 +140,4 @@ public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, S
} }
public static void openSharedEnderChest(PlayerEntity player) {
player.openHandledScreen(new SimpleNamedScreenHandlerFactory((int_1, playerInventory, playerEntity) ->
new GenericContainerScreenHandler(getConfig().screenHandlerType(), int_1, playerInventory, sharedInventory, getConfig().sharedEnderChestRows), Text.of(getConfig().sharedEnderChestName)));
}
public static void playEnderChestOpenSound(World world, BlockPos pos) {
world.playSound(null, pos, SoundEvents.BLOCK_ENDER_CHEST_OPEN, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F);
}
private static File getFile(MinecraftServer server) {
return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile();
}
} }

View File

@ -1,13 +1,18 @@
package wtf.hak.survivalfabric.sharedenderchest; package wtf.hak.survivalfabric.features.sharedenderchest;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventories; import net.minecraft.inventory.Inventories;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos;
import java.util.HashMap;
import java.util.Map;
public class SharedInventory implements Inventory { public class SharedInventory implements Inventory {
public final Map<PlayerEntity, BlockPos> openedEnderChests = new HashMap<>();
private final DefaultedList<ItemStack> stacks; private final DefaultedList<ItemStack> stacks;
public SharedInventory(int inventoryRows) { public SharedInventory(int inventoryRows) {
@ -40,7 +45,7 @@ public class SharedInventory implements Inventory {
} }
itemStack_1 = var1.next(); itemStack_1 = var1.next();
} while(itemStack_1.isEmpty()); } while (itemStack_1.isEmpty());
return false; return false;
} }
@ -52,8 +57,7 @@ public class SharedInventory implements Inventory {
@Override @Override
public ItemStack removeStack(int int_1, int int_2) { public ItemStack removeStack(int int_1, int int_2) {
ItemStack itemStack_1 = Inventories.splitStack(this.stacks, int_1, int_2); return Inventories.splitStack(this.stacks, int_1, int_2);
return itemStack_1;
} }
@Override @Override
@ -80,4 +84,12 @@ public class SharedInventory implements Inventory {
public void clear() { public void clear() {
stacks.clear(); stacks.clear();
} }
@Override
public void onClose(PlayerEntity player) {
BlockPos pos = openedEnderChests.remove(player);
if (openedEnderChests.containsValue(pos))
return;
SharedEnderChest.fakeEnderChestOpen(player.getWorld(), pos, false);
}
} }

View File

@ -1,10 +1,12 @@
package wtf.hak.survivalfabric.veinminer; package wtf.hak.survivalfabric.features.veinminer;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
public interface Drill { public interface Drill {
boolean canHandle(BlockState blockState); boolean canHandle(BlockState blockState);
boolean isRightTool(BlockPos pos); boolean isRightTool(BlockPos pos);
boolean drill(BlockPos blockPos); boolean drill(BlockPos blockPos);
} }

View File

@ -1,15 +1,14 @@
package wtf.hak.survivalfabric.veinminer; package wtf.hak.survivalfabric.features.veinminer;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import wtf.hak.survivalfabric.veinminer.drills.LeavesDrill; import wtf.hak.survivalfabric.features.veinminer.drills.LeavesDrill;
import wtf.hak.survivalfabric.veinminer.drills.OreDrill; import wtf.hak.survivalfabric.features.veinminer.drills.OreDrill;
import wtf.hak.survivalfabric.veinminer.drills.WoodDrill; import wtf.hak.survivalfabric.features.veinminer.drills.WoodDrill;
public class VeinMinerEvents { public class VeinMinerEvents {
@ -21,26 +20,25 @@ public class VeinMinerEvents {
boolean isVeinMining = VeinMinerSession.sessionForPlayer(player) != null; boolean isVeinMining = VeinMinerSession.sessionForPlayer(player) != null;
boolean canVeinMine = player.isInSneakingPose(); boolean canVeinMine = player.isInSneakingPose();
if (canVeinMine && !isVeinMining) { if (canVeinMine && !isVeinMining) {
VeinMinerSession session = VeinMinerSession.start(player, (ServerWorld)world, pos); VeinMinerSession session = VeinMinerSession.start(player, (ServerWorld) world, pos);
boolean shouldContinue = !mine(session); boolean shouldContinue = !mine(session);
session.finish(); session.finish();
return shouldContinue; return shouldContinue;
} } else {
else {
return true; return true;
} }
} }
public static void onEntityLoad(Entity entity, ServerWorld world) { public static void onEntityLoad(Entity entity, ServerWorld world) {
BlockPos pos = entity.getBlockPos(); BlockPos pos = entity.getBlockPos();
VeinMinerSession session = VeinMinerSession.sessionForPosition(pos); VeinMinerSession session = VeinMinerSession.sessionForPosition(pos);
if (session != null) { if (session != null) {
entity.setPos(session.initialPos.getX(), session.initialPos.getY(), session.initialPos.getZ()); entity.setPos(session.initialPos.getX(), session.initialPos.getY(), session.initialPos.getZ());
} }
} }
private static boolean mine(VeinMinerSession session) { private static boolean mine(VeinMinerSession session) {
Drill[] drills = new Drill[] { Drill[] drills = new Drill[]{
new OreDrill(session), new OreDrill(session),
new WoodDrill(session), new WoodDrill(session),
new LeavesDrill(session) new LeavesDrill(session)

View File

@ -1,23 +1,31 @@
package wtf.hak.survivalfabric.veinminer; package wtf.hak.survivalfabric.features.veinminer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class VeinMinerSession { public class VeinMinerSession {
private static ArrayList<VeinMinerSession> sessions = new ArrayList<>(); private static final ArrayList<VeinMinerSession> sessions = new ArrayList<>();
public ServerPlayerEntity player; public ServerPlayerEntity player;
public ServerWorld world; public ServerWorld world;
public Set<BlockPos> positions; public Set<BlockPos> positions;
public BlockPos initialPos; public BlockPos initialPos;
private VeinMinerSession(ServerPlayerEntity player, ServerWorld world, BlockPos initialPos) {
this.player = player;
this.world = world;
this.initialPos = initialPos;
this.positions = new HashSet<>();
positions.add(initialPos);
}
public static VeinMinerSession sessionForPlayer(ServerPlayerEntity player) { public static VeinMinerSession sessionForPlayer(ServerPlayerEntity player) {
for (var session: sessions) { for (var session : sessions) {
if (session.player == player) { if (session.player == player) {
return session; return session;
} }
@ -26,7 +34,7 @@ public class VeinMinerSession {
} }
public static VeinMinerSession sessionForPosition(BlockPos position) { public static VeinMinerSession sessionForPosition(BlockPos position) {
for (var session: sessions) { for (var session : sessions) {
if (session.positions.contains(position)) { if (session.positions.contains(position)) {
return session; return session;
} }
@ -44,14 +52,6 @@ public class VeinMinerSession {
sessions.remove(session); sessions.remove(session);
} }
private VeinMinerSession(ServerPlayerEntity player, ServerWorld world, BlockPos initialPos) {
this.player = player;
this.world = world;
this.initialPos = initialPos;
this.positions = new HashSet<>();
positions.add(initialPos);
}
public void addPosition(BlockPos pos) { public void addPosition(BlockPos pos) {
positions.add(pos); positions.add(pos);
} }

View File

@ -1,15 +1,15 @@
package wtf.hak.survivalfabric.veinminer.drills; package wtf.hak.survivalfabric.features.veinminer.drills;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import java.util.ArrayList;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.PlainTextContent; import net.minecraft.text.PlainTextContent;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.Drill; import wtf.hak.survivalfabric.features.veinminer.Drill;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession;
import java.util.ArrayList;
import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER; import static wtf.hak.survivalfabric.SurvivalFabric.LOGGER;
@ -37,14 +37,6 @@ public class DrillBase implements Drill {
return session.player.getMainHandStack().isSuitableFor(blockState); return session.player.getMainHandStack().isSuitableFor(blockState);
} }
protected interface ForXYZHandler {
public void handle(BlockPos pos);
}
protected interface ForXYZCounter {
public int handle(BlockPos pos);
}
protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler) { protected void forXYZ(BlockPos pos, int max, ForXYZHandler handler) {
forXYZ(pos, max, handlerPos -> { forXYZ(pos, max, handlerPos -> {
handler.handle(handlerPos); handler.handle(handlerPos);
@ -72,33 +64,30 @@ public class DrillBase implements Drill {
} }
} }
String[] order = new String[] { "x", "y", "z" }; String[] order = new String[]{"x", "y", "z"};
if (forceVertical) { if (forceVertical) {
order = new String[] { "y", "x", "z" }; order = new String[]{"y", "x", "z"};
} } else {
else {
ServerPlayerEntity player = session.player; ServerPlayerEntity player = session.player;
boolean majorPitchChange = player.getPitch() < -45.0 || player.getPitch() > 45.0; boolean majorPitchChange = player.getPitch() < -45.0 || player.getPitch() > 45.0;
boolean majorYawChange = (player.getYaw() > 45.0 && player.getYaw() < 135.0) || (player.getYaw() < -45.0 && player.getYaw() > -135.0); boolean majorYawChange = (player.getYaw() > 45.0 && player.getYaw() < 135.0) || (player.getYaw() < -45.0 && player.getYaw() > -135.0);
if (majorPitchChange) { if (majorPitchChange) {
if (majorYawChange) { if (majorYawChange) {
order = new String[] { "y", "z", "x" }; order = new String[]{"y", "z", "x"};
} else {
order = new String[]{"y", "x", "z"};
} }
else { } else {
order = new String[] { "y", "x", "z" };
}
}
else {
if (majorYawChange) { if (majorYawChange) {
order = new String[] { "z", "y", "x" }; order = new String[]{"z", "y", "x"};
} }
} }
} }
int counter = 0; int counter = 0;
for (int i1: offsets) { for (int i1 : offsets) {
for (int i2: offsets) { for (int i2 : offsets) {
for (int i3: offsets) { for (int i3 : offsets) {
int ix = order[0] == "x" ? i1 : order[1] == "x" ? i2 : i3; int ix = order[0] == "x" ? i1 : order[1] == "x" ? i2 : i3;
int iy = order[0] == "y" ? i1 : order[1] == "y" ? i2 : i3; int iy = order[0] == "y" ? i1 : order[1] == "y" ? i2 : i3;
int iz = order[0] == "z" ? i1 : order[1] == "z" ? i2 : i3; int iz = order[0] == "z" ? i1 : order[1] == "z" ? i2 : i3;
@ -130,4 +119,12 @@ public class DrillBase implements Drill {
session.player.sendMessage(text); session.player.sendMessage(text);
LOGGER.info(message); LOGGER.info(message);
} }
protected interface ForXYZHandler {
void handle(BlockPos pos);
}
protected interface ForXYZCounter {
int handle(BlockPos pos);
}
} }

View File

@ -1,4 +1,4 @@
package wtf.hak.survivalfabric.veinminer.drills; package wtf.hak.survivalfabric.features.veinminer.drills;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -7,7 +7,7 @@ import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession; import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -15,12 +15,12 @@ import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class LeavesDrill extends DrillBase { public class LeavesDrill extends DrillBase {
public static final TagKey<Block> leavesTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves"));
public LeavesDrill(VeinMinerSession session) { public LeavesDrill(VeinMinerSession session) {
super(session); super(session);
} }
public static final TagKey<Block> leavesTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "leaves"));
@Override @Override
public boolean canHandle(BlockState blockState) { public boolean canHandle(BlockState blockState) {
return blockState.isIn(leavesTag); return blockState.isIn(leavesTag);

View File

@ -1,4 +1,4 @@
package wtf.hak.survivalfabric.veinminer.drills; package wtf.hak.survivalfabric.features.veinminer.drills;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -6,23 +6,21 @@ import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey; import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class OreDrill extends DrillBase { public class OreDrill extends DrillBase {
public static final TagKey<Block> oreTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore"));
public OreDrill(VeinMinerSession session) { public OreDrill(VeinMinerSession session) {
super(session); super(session);
} }
public static final TagKey<Block> oreTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "ore"));
@Override @Override
public boolean canHandle(BlockState blockState) { public boolean canHandle(BlockState blockState) {
return blockState.isIn(oreTag); return blockState.isIn(oreTag);

View File

@ -1,29 +1,27 @@
package wtf.hak.survivalfabric.veinminer.drills; package wtf.hak.survivalfabric.features.veinminer.drills;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey; import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.features.veinminer.VeinMinerSession;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import net.minecraft.block.Block;
import net.minecraft.registry.Registries;
import net.minecraft.util.math.BlockPos;
import wtf.hak.survivalfabric.veinminer.VeinMinerSession;
import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; import static wtf.hak.survivalfabric.config.ConfigManager.getConfig;
public class WoodDrill extends DrillBase { public class WoodDrill extends DrillBase {
public static final TagKey<Block> woodTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood"));
public WoodDrill(VeinMinerSession session) { public WoodDrill(VeinMinerSession session) {
super(session); super(session);
} }
public static final TagKey<Block> woodTag = TagKey.of(RegistryKeys.BLOCK, Identifier.of("survivalfabric", "wood"));
@Override @Override
public boolean canHandle(BlockState blockState) { public boolean canHandle(BlockState blockState) {
return blockState.isIn(woodTag); return blockState.isIn(woodTag);
@ -33,8 +31,8 @@ public class WoodDrill extends DrillBase {
public boolean drill(BlockPos startPos) { public boolean drill(BlockPos startPos) {
ServerWorld world = session.world; ServerWorld world = session.world;
int broken = 0; int broken = 0;
ArrayDeque<BlockPos> pendingLogs = new ArrayDeque<BlockPos>(); ArrayDeque<BlockPos> pendingLogs = new ArrayDeque<>();
ArrayDeque<BlockPos> logBlocks = new ArrayDeque<BlockPos>(); ArrayDeque<BlockPos> logBlocks = new ArrayDeque<>();
pendingLogs.add(startPos); pendingLogs.add(startPos);
String leavesBlockId = Registries.BLOCK.getId(world.getBlockState(startPos).getBlock()).toString().replace("_log", "_leaves"); String leavesBlockId = Registries.BLOCK.getId(world.getBlockState(startPos).getBlock()).toString().replace("_log", "_leaves");
@ -57,11 +55,8 @@ public class WoodDrill extends DrillBase {
} }
} }
// second round, leaves
// The pending blocks are all air now,
ArrayDeque<BlockPos> pendingLeaves = logBlocks; ArrayDeque<BlockPos> pendingLeaves = logBlocks;
while (!pendingLeaves.isEmpty() && broken < getConfig().maxVeinSize) { while (!pendingLeaves.isEmpty() && broken < getConfig().maxVeinSize) {
// remove the immediately surrounding leaves around the log blocks
broken += forXYZ(pendingLeaves.remove(), 1, newPos -> { broken += forXYZ(pendingLeaves.remove(), 1, newPos -> {
int brokenLeaves = 0; int brokenLeaves = 0;
Block newBlock = world.getBlockState(newPos).getBlock(); Block newBlock = world.getBlockState(newPos).getBlock();

View File

@ -1,10 +1,8 @@
package wtf.hak.survivalfabric.mixin; package wtf.hak.survivalfabric.mixin;
import com.mojang.authlib.minecraft.client.MinecraftClient;
import net.minecraft.network.ClientConnection; import net.minecraft.network.ClientConnection;
import net.minecraft.network.message.MessageType; import net.minecraft.network.message.MessageType;
import net.minecraft.network.message.SignedMessage; import net.minecraft.network.message.SignedMessage;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager; import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ConnectedClientData; import net.minecraft.server.network.ConnectedClientData;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
@ -18,9 +16,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import wtf.hak.survivalfabric.commands.SpectatorCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand;
import wtf.hak.survivalfabric.config.ConfigManager; import wtf.hak.survivalfabric.config.ConfigManager;
import java.awt.*;
import java.beans.Expression;
import java.util.Objects;
import java.util.Set; import java.util.Set;
@Mixin(PlayerManager.class) @Mixin(PlayerManager.class)
@ -28,7 +23,7 @@ public abstract class PlayerManagerMixin {
@Inject(method = {"onPlayerConnect"}, at = {@At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")}) @Inject(method = {"onPlayerConnect"}, at = {@At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")})
public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) { public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) {
if(ConfigManager.getConfig().joinMessageEnabled && !player.getServer().isSingleplayer()) { if (ConfigManager.getConfig().joinMessageEnabled && !player.getServer().isSingleplayer()) {
Text text = Text.literal(String.format(ConfigManager.getConfig().joinMessage, player.getName().getString())); Text text = Text.literal(String.format(ConfigManager.getConfig().joinMessage, player.getName().getString()));
player.sendMessage(text, false); player.sendMessage(text, false);
} }
@ -36,7 +31,7 @@ public abstract class PlayerManagerMixin {
@ModifyArg(method = {"onPlayerConnect"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")) @ModifyArg(method = {"onPlayerConnect"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V"))
private Text onPlayerConnect(Text text) { private Text onPlayerConnect(Text text) {
if(ConfigManager.getConfig().joinMessageEnabled) { if (ConfigManager.getConfig().joinMessageEnabled) {
String name = text.getString().split(" ")[0]; String name = text.getString().split(" ")[0];
return Text.literal(String.format(ConfigManager.getConfig().joinMessage, name)); return Text.literal(String.format(ConfigManager.getConfig().joinMessage, name));
} else } else
@ -45,42 +40,48 @@ public abstract class PlayerManagerMixin {
@Inject(method = {"remove"}, at = {@At("HEAD")}) @Inject(method = {"remove"}, at = {@At("HEAD")})
public void onPlayerLeave(ServerPlayerEntity player, CallbackInfo ci) { public void onPlayerLeave(ServerPlayerEntity player, CallbackInfo ci) {
if(SpectatorCommand.spectating.containsKey(player)) { if (SpectatorCommand.spectating.containsKey(player)) {
SpectatorCommand.LocationData loc = SpectatorCommand.spectating.remove(player); SpectatorCommand.LocationData loc = SpectatorCommand.spectating.remove(player);
player.teleport(loc.world, loc.x,loc.y,loc.z, Set.of(), loc.yaw, loc.pitch, false); player.teleport(loc.world, loc.x, loc.y, loc.z, Set.of(), loc.yaw, loc.pitch, false);
player.changeGameMode(GameMode.SURVIVAL); player.changeGameMode(GameMode.SURVIVAL);
} }
} }
@Inject(method = {"broadcast(Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/network/message/MessageType$Parameters;)V"}, at = {@At("HEAD")}, cancellable = true) @Inject(method = {"broadcast(Lnet/minecraft/network/message/SignedMessage;Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/network/message/MessageType$Parameters;)V"}, at = {@At("HEAD")}, cancellable = true)
private void onBroadcast(SignedMessage message, ServerPlayerEntity sender, MessageType.Parameters parameters, CallbackInfo ci) { private void onBroadcast(SignedMessage message, ServerPlayerEntity sender, MessageType.Parameters parameters, CallbackInfo ci) {
if(sender != null && ConfigManager.getConfig().chatMessageEnabled) { if (sender != null) {
String rawMessage = message.getContent().getString().trim(); String rawMessage = message.getContent().getString().trim();
if(sender != null && ConfigManager.getConfig().chatCalcEnabled && rawMessage.endsWith("=")) { boolean isCalcEnabled = ConfigManager.getConfig().chatCalcEnabled;
String expression = rawMessage.substring(0, rawMessage.length() - 1).trim(); boolean isMsgEnabled = ConfigManager.getConfig().chatMessageEnabled;
String processedMessage = rawMessage;
if (isCalcEnabled) {
String expression = rawMessage.endsWith("=") ? rawMessage.substring(0, rawMessage.length() - 1).trim() : rawMessage;
try { try {
String result = String.valueOf(evaluateExpression(expression)); String result = String.valueOf(evaluateExpression(expression));
if(rawMessage.contains(" ")) rawMessage += " "; StringBuilder sb = new StringBuilder(rawMessage).append("§6");
rawMessage += (result.endsWith(".0")) ? result.substring(0, result.length() - 2) : result;
} catch (Exception e) {} if (rawMessage.contains(" ")) sb.append(" ");
if (!rawMessage.endsWith("=")) sb.append("=");
if (rawMessage.contains(" ")) sb.append(" ");
sb.append(result.endsWith(".0") ? result.substring(0, result.length() - 2) : result);
processedMessage = sb.toString();
} catch (Exception ignored) {}
} }
Text text = Text.literal(String.format(ConfigManager.getConfig().chatMessage, sender.getName().getString(), rawMessage));
sender.getServer().getPlayerManager().broadcast(text, false); if (isMsgEnabled) {
String formatted = String.format(ConfigManager.getConfig().chatMessage, sender.getName().getString(), processedMessage);
sender.getServer().getPlayerManager().broadcast(Text.literal(formatted), false);
} else if (isCalcEnabled) {
String formatted = "<" + sender.getName().getString() + "> " + processedMessage;
sender.getServer().getPlayerManager().broadcast(Text.literal(formatted), false);
}
ci.cancel(); ci.cancel();
} else if (sender != null && ConfigManager.getConfig().chatCalcEnabled) {
String rawMessage = message.getContent().getString().trim();
if (rawMessage.endsWith("=")) {
String expression = rawMessage.substring(0, rawMessage.length() - 1).trim();
try {
String result = String.valueOf(evaluateExpression(expression));
if(rawMessage.contains(" ")) rawMessage += " ";
rawMessage += (result.endsWith(".0")) ? result.substring(0, result.length() - 2) : result;
Text formattedMessage = Text.literal("<" + sender.getName().getString() + "> " + rawMessage);
sender.getServer().getPlayerManager().broadcast(formattedMessage, false);
ci.cancel();
} catch (Exception e) {}
}
} }
} }
private double evaluateExpression(String expression) { private double evaluateExpression(String expression) {

View File

@ -12,7 +12,7 @@ public abstract class ServerPlayNetworkHandlerMixin {
@ModifyArg(method = {"cleanUp"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V")) @ModifyArg(method = {"cleanUp"}, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;broadcast(Lnet/minecraft/text/Text;Z)V"))
private Text quitMessage(Text text) { private Text quitMessage(Text text) {
if(ConfigManager.getConfig().quitMessageEnabled) { if (ConfigManager.getConfig().quitMessageEnabled) {
String name = text.getString().split(" ")[0]; String name = text.getString().split(" ")[0];
return Text.literal(String.format(ConfigManager.getConfig().quitMessage, name)); return Text.literal(String.format(ConfigManager.getConfig().quitMessage, name));
} }

View File

@ -14,7 +14,7 @@ public abstract class ServerPlayerEntityMixin {
@Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true) @Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true)
private void changePlayerListName(CallbackInfoReturnable<Text> cir) { private void changePlayerListName(CallbackInfoReturnable<Text> cir) {
if(ConfigManager.getConfig().dimensionIndicatorEnabled) { if (ConfigManager.getConfig().dimensionIndicatorEnabled) {
ServerPlayerEntity p = (ServerPlayerEntity) (Object) this; ServerPlayerEntity p = (ServerPlayerEntity) (Object) this;
String world = p.getServerWorld().getRegistryKey().getValue().toTranslationKey(); String world = p.getServerWorld().getRegistryKey().getValue().toTranslationKey();
String finalName; String finalName;

View File

@ -7,15 +7,15 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import wtf.hak.survivalfabric.utils.Utils; import wtf.hak.survivalfabric.utils.PacketUtils;
@Mixin(ServerWorld.class) @Mixin(ServerWorld.class)
public class ServerWorldMixin { public class ServerWorldMixin {
@Inject(method = "onDimensionChanged", at = {@At("HEAD")}) @Inject(method = "onDimensionChanged", at = {@At("HEAD")})
public void onDimensionChange(Entity entity, CallbackInfo ci) { public void onDimensionChange(Entity entity, CallbackInfo ci) {
if(entity instanceof ServerPlayerEntity) { if (entity instanceof ServerPlayerEntity) {
Utils.updateListNames((ServerPlayerEntity)entity); PacketUtils.updateListNames((ServerPlayerEntity) entity);
} }
} }
} }

View File

@ -5,10 +5,10 @@ import net.minecraft.server.network.ServerPlayerEntity;
import java.util.Objects; import java.util.Objects;
public class Utils { public class PacketUtils {
public static void updateListNames(ServerPlayerEntity p) { public static void updateListNames(ServerPlayerEntity p) {
for(ServerPlayerEntity sp : Objects.requireNonNull(p.getServer()).getPlayerManager().getPlayerList()) { for (ServerPlayerEntity sp : Objects.requireNonNull(p.getServer()).getPlayerManager().getPlayerList()) {
sp.networkHandler.sendPacket(new PlayerListS2CPacket(PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, p)); sp.networkHandler.sendPacket(new PlayerListS2CPacket(PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, p));
} }
} }

View File

@ -15,5 +15,7 @@
"key.survivalfabric.angle12": "204.81 / -54.23", "key.survivalfabric.angle12": "204.81 / -54.23",
"key.survivalfabric.angle13": "245.14 / -54.23", "key.survivalfabric.angle13": "245.14 / -54.23",
"key.survivalfabric.angle14": "204.98 / -41.68", "key.survivalfabric.angle14": "204.98 / -41.68",
"key.survivalfabric.angle15": "244.97 / -41.71" "key.survivalfabric.angle15": "244.97 / -41.71",
"category.survivalfabric.survivalfabric": "Survival Fabric",
"key.survivalfabric.camera": "/camera"
} }

View File

@ -1,48 +1,48 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "survivalfabric", "id": "survivalfabric",
"version": "${version}", "version": "${version}",
"name": "SurvivalFabric", "name": "SurvivalFabric",
"description": "Adds a few QOL features to your Survival!", "description": "Adds a few QOL features to your Survival!",
"authors": [ "authors": [
"AlwaysHAK" "AlwaysHAK"
], ],
"contact": { "contact": {
"homepage": "https://hak.wtf", "homepage": "https://hak.wtf",
"sources": "https://git.hak.wtf/hkuijlman/SurvivalFabric", "sources": "https://git.hak.wtf/hkuijlman/SurvivalFabric",
"issues": "https://git.hak.wtf/hkuijlman/SurvivalFabric/issues", "issues": "https://git.hak.wtf/hkuijlman/SurvivalFabric/issues"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
"icon": "assets/survivalfabric/icon.png", "icon": "assets/survivalfabric/icon.png",
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"main": [ "main": [
"wtf.hak.survivalfabric.SurvivalFabric" "wtf.hak.survivalfabric.SurvivalFabric"
], ],
"client": [ "client": [
"wtf.hak.survivalfabric.SurvivalFabricClient" "wtf.hak.survivalfabric.SurvivalFabricClient"
], ],
"fabric-datagen": [ "fabric-datagen": [
"wtf.hak.survivalfabric.SurvivalFabricDataGenerator" "wtf.hak.survivalfabric.SurvivalFabricDataGenerator"
], ],
"modmenu": [ "modmenu": [
"wtf.hak.survivalfabric.modmenu.ModMenuIntegration" "wtf.hak.survivalfabric.modmenu.ModMenuIntegration"
] ]
}, },
"mixins": [ "mixins": [
"survivalfabric.mixins.json", "survivalfabric.mixins.json",
{ {
"config": "survivalfabric.client.mixins.json", "config": "survivalfabric.client.mixins.json",
"environment": "client" "environment": "client"
} }
], ],
"depends": { "depends": {
"fabricloader": ">=0.16.10", "fabricloader": ">=0.16.10",
"minecraft": "~1.21.5", "minecraft": "~1.21.5",
"java": ">=21", "java": ">=21",
"fabric-api": "*" "fabric-api": "*"
}, },
"optional": { "optional": {
"modmenu": "*" "modmenu": "*"
} }
} }

View File

@ -10,5 +10,5 @@
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
} }