23 Commits

Author SHA1 Message Date
38a244b023 Added correct ModMenu version for 1.21.4
All checks were successful
build / build (push) Successful in 1m21s
2025-04-07 10:37:17 +02:00
868e71eb10 Fixed typo 2025-04-07 10:37:07 +02:00
2a106c9a03 Merge branch 'master' into 1.21.4
# Conflicts:
#	gradle.properties
2025-04-07 10:33:20 +02:00
8c3798d456 Created basic Mod Menu Integration and made different fog types toggleable 2025-04-07 10:32:39 +02:00
d99467d953 Added ModMenu as (optional) dependency 2025-04-07 09:18:14 +02:00
1d0f7f0f05 Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m9s
2025-04-06 12:25:41 +02:00
7d969d0013 Removed Game Fog (cleaned up AngleViewer code)
All checks were successful
build / build (push) Successful in 1m2s
2025-04-06 12:25:30 +02:00
52a97dc2c1 Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m11s
2025-03-30 23:33:15 +02:00
0fede8adbf Added Chat Calculator
All checks were successful
build / build (push) Successful in 1m7s
2025-03-30 23:32:43 +02:00
5916cc5cc2 Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m2s
2025-03-30 18:16:16 +02:00
31afdab2cf Reverted head movement unlock on teleport due to bugs
All checks were successful
build / build (push) Successful in 1m6s
revert Made head movement lock expire as soon as teleported
2025-03-30 18:15:18 +02:00
4e58bede4b Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m5s
2025-03-30 17:58:58 +02:00
7db6d01869 Made head movement lock expire as soon as teleported
All checks were successful
build / build (push) Successful in 1m2s
2025-03-30 17:57:57 +02:00
130b2db727 Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m8s
2025-03-30 17:28:42 +02:00
16027c04d1 Made head movement locked while "angle viewing"
All checks were successful
build / build (push) Successful in 1m6s
2025-03-30 17:26:55 +02:00
c993621bdb Merge branch 'master' into 1.21.4
All checks were successful
build / build (push) Successful in 1m7s
2025-03-30 17:13:15 +02:00
8ae9a4f7c4 Fixed annoyance 2025-03-30 17:12:27 +02:00
b89ab99928 Downgraded mod to 1.21.4
All checks were successful
build / build (push) Successful in 1m8s
2025-03-30 17:06:42 +02:00
cae8c34759 Added Teleportation Angle Viewer
All checks were successful
build / build (push) Successful in 1m8s
(Also removed FPS counter)
2025-03-30 16:16:51 +02:00
77ae4cc1f0 Removed Magma Blocks and added Quartz Ore to veinmine 2025-03-30 14:45:44 +02:00
e465963daa Created 'build.yml' workflow
All checks were successful
build / build (push) Successful in 4m0s
2025-03-27 13:19:22 +01:00
6b862dfbc3 Deleted duplicate class 2025-03-27 11:31:52 +01:00
045623a67b Split client & common sources + POC FPS counter 2025-03-27 11:03:35 +01:00
20 changed files with 526 additions and 24 deletions

View File

@ -0,0 +1,32 @@
name: build
on:
push:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: Setup JDK
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'microsoft'
- name: Make Gradle wrapper executable
run: chmod +x ./gradlew
- name: Build
run: ./gradlew build
- name: Capture build artifacts
uses: actions/upload-artifact@v3
with:
name: Artifacts
path: build/libs/

View File

@ -4,7 +4,9 @@
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. (It ain't there yet tho ;) )
## Current feature-set # Current feature-set
## Server Side
### Features ### Features
- Custom join message - Custom join message
@ -27,16 +29,25 @@ As a challenge I'm trying to make it as user-friendly as possible. (It ain't the
- Vein miner - Vein miner
![VeinMiner](https://i.imgur.com/zOXWMNa.gif) ![VeinMiner](https://i.imgur.com/zOXWMNa.gif)
### 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
### Misc # Currently working on 1.3.2
- Updated icon
## Features to come ## Server Side
- [x] Chat Calculator
## 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
## Server Side
- Telekinesis - Telekinesis
Other than that no more features! Other than that no more features!

View File

@ -11,11 +11,22 @@ base {
} }
repositories { repositories {
// Add repositories to retrieve artifacts from in here. maven {
// You should only use this when depending on other mods because name = "Terraformers"
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically. url = "https://maven.terraformersmc.com/"
// See https://docs.gradle.org/current/userguide/declaring_repositories.html }
// for more information about repositories. }
loom {
splitEnvironmentSourceSets()
mods {
"survivalfabric" {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
} }
fabricApi { fabricApi {
@ -26,13 +37,13 @@ fabricApi {
dependencies { dependencies {
// To change the versions see the gradle.properties file // To change the versions see the gradle.properties file
minecraft "net.minecraft:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway. // Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}")
} }
processResources { processResources {

View File

@ -4,14 +4,16 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.21.5 minecraft_version=1.21.4
yarn_mappings=1.21.5+build.1 yarn_mappings=1.21.4+build.8
loader_version=0.16.10 loader_version=0.16.10
# Mod Properties # Mod Properties
mod_version=1.3.1 mod_version=1.3.2
maven_group=wtf.hak.survivalfabric maven_group=wtf.hak.survivalfabric
archives_base_name=survivalfabric archives_base_name=survivalfabric
# Dependencies # Dependencies
fabric_version=0.119.5+1.21.5 fabric_version=0.119.0+1.21.4
modmenu_version=13.0.3

0
gradlew vendored Normal file → Executable file
View File

View File

@ -0,0 +1,14 @@
package wtf.hak.survivalfabric;
import net.fabricmc.api.ClientModInitializer;
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
import wtf.hak.survivalfabric.features.AngleViewer;
public class SurvivalFabricClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ClientConfigManager.getConfig();
AngleViewer.register();
}
}

View File

@ -0,0 +1,12 @@
package wtf.hak.survivalfabric.config.client;
public class ClientConfig {
public String configVersion = "1.0";
public boolean renderNetherFog = false;
public boolean renderOverworldFog = false;
public boolean renderEndFog = false;
public boolean renderLavaFog = false;
public boolean renderWaterFog = false;
public boolean renderSnowFog = false;
}

View File

@ -0,0 +1,55 @@
package wtf.hak.survivalfabric.config.client;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.fabricmc.loader.api.FabricLoader;
import wtf.hak.survivalfabric.config.Config;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class ClientConfigManager {
private static final File CONFIG_FILE = FabricLoader.getInstance().getConfigDir().resolve("survivalfabric-client.json").toFile();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static ClientConfig INSTANCE;
public static ClientConfig getConfig() {
if (INSTANCE == null) {
return load();
} else
return INSTANCE;
}
public static ClientConfig load() {
try(FileReader reader = new FileReader(CONFIG_FILE)) {
INSTANCE = GSON.fromJson(reader, ClientConfig.class);
if (INSTANCE.configVersion.equalsIgnoreCase(new Config().configVersion)) {
return INSTANCE;
}
INSTANCE.configVersion = new ClientConfig().configVersion;
save(INSTANCE);
return INSTANCE;
} catch (IOException e) {
ClientConfig config = new ClientConfig();
INSTANCE = config;
save(config);
return config;
}
}
public static void save(){
save(INSTANCE);
}
public static void save(ClientConfig config) {
try(FileWriter writer = new FileWriter(CONFIG_FILE)) {
GSON.toJson(config, writer);
} catch (IOException e) {
System.out.println("Error saving config: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,75 @@
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.network.ClientPlayerEntity;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
import org.lwjgl.glfw.GLFW;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class AngleViewer {
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public static boolean PREVENT_HEAD_MOVEMENT = false;
public static void register() {
for(Angle angle : Angle.values()) {
KeyBinding keyBinding = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.survivalfabric." + angle.name().toLowerCase(),
InputUtil.Type.KEYSYM,
GLFW.GLFW_DONT_CARE,
"category.survivalfabric.tpangles"
));
ClientTickEvents.END_CLIENT_TICK.register(mc -> {
while (keyBinding.wasPressed()) {
ClientPlayerEntity player = mc.player;
if(player == null) return;
player.setYaw(angle.yaw);
player.setPitch(angle.pitch);
PREVENT_HEAD_MOVEMENT = true;
scheduler.schedule(() -> {
if(player == null) return;
PREVENT_HEAD_MOVEMENT = false;
player.setPitch(-90);
PREVENT_HEAD_MOVEMENT = true;
scheduler.schedule(() -> PREVENT_HEAD_MOVEMENT = false, 1500, TimeUnit.MILLISECONDS);
}, 1500, TimeUnit.MILLISECONDS);
}
});
}
}
public enum Angle {
ANGLE0(-65.19f, -54.23f),
ANGLE1(-24.86f, -54.23f),
ANGLE2(-65.02f, -41.68f),
ANGLE3(-25.03f, -41.71f),
ANGLE4(24.81f, -54.23f),
ANGLE5(65.14f, -54.23f),
ANGLE6(24.98f, -41.68f),
ANGLE7(64.97f, -41.71f),
ANGLE8(114.81f, -54.23f),
ANGLE9(155.14f, -54.23f),
ANGLE10(114.98f, -41.68f),
ANGLE11(154.97f, -41.71f),
ANGLE12(204.81f, -54.23f),
ANGLE13(245.14f, -54.23f),
ANGLE14(204.98f, -41.68f),
ANGLE15(244.97f, -41.71f);
public final float yaw;
public final float pitch;
Angle(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
}
}

View File

@ -0,0 +1,42 @@
package wtf.hak.survivalfabric.mixin.client;
import net.minecraft.block.enums.CameraSubmersionType;
import net.minecraft.client.render.BackgroundRenderer;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Fog;
import net.minecraft.client.render.FogShape;
import net.minecraft.world.World;
import org.joml.Vector4f;
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.CallbackInfoReturnable;
import static wtf.hak.survivalfabric.config.client.ClientConfigManager.getConfig;
@Mixin(value = BackgroundRenderer.class, priority = 910)
public abstract class BackgroundRendererMixin {
@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) {
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)
cir.setReturnValue(new Fog(-8.0f, 1_000_000.0F, FogShape.CYLINDER, 0,0,0,0));
}
}

View File

@ -0,0 +1,31 @@
package wtf.hak.survivalfabric.mixin.client;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.Entity;
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;
import wtf.hak.survivalfabric.features.AngleViewer;
@Mixin(Entity.class)
public abstract class EntityMixin {
@Inject(method = "setYaw", at = @At("HEAD"), cancellable = true)
private void preventYawChange(float yaw, CallbackInfo ci) {
if((Object) this instanceof ClientPlayerEntity player) {
if(player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT) {
ci.cancel();
}
}
}
@Inject(method = "setPitch", at = @At("HEAD"), cancellable = true)
private void preventPitchChange(float pitch, CallbackInfo ci) {
if((Object) this instanceof ClientPlayerEntity player) {
if(player.isMainPlayer() && AngleViewer.PREVENT_HEAD_MOVEMENT) {
ci.cancel();
}
}
}
}

View File

@ -0,0 +1,88 @@
package wtf.hak.survivalfabric.modmenu;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import wtf.hak.survivalfabric.config.client.ClientConfig;
import wtf.hak.survivalfabric.config.client.ClientConfigManager;
import java.lang.reflect.Field;
public class ConfigScreen extends Screen {
private final Screen parent;
public ConfigScreen(Screen parent) {
super(Text.literal("Survival Fabric - Client Config"));
this.parent = parent;
}
@Override
protected void init() {
int buttonWidth = 200;
int buttonHeight = 20;
int spacing = 5;
int startY = 40;
int i = 0;
for (Field field : ClientConfig.class.getFields()) {
if (field.getType() == boolean.class) {
int y = startY + i * (buttonHeight + spacing);
try {
boolean value = field.getBoolean(ClientConfigManager.getConfig());
String label = formatFieldName(field.getName()) + ": " + (value ? "ON" : "OFF");
ButtonWidget button = ButtonWidget.builder(
Text.literal(label),
b -> {
try {
boolean current = field.getBoolean(ClientConfigManager.getConfig());
field.setBoolean(ClientConfigManager.getConfig(), !current);
b.setMessage(Text.literal(formatFieldName(field.getName()) + ": " + (!current ? "ON" : "OFF")));
ClientConfigManager.save(); // Save if needed
} catch (Exception e) {
e.printStackTrace();
}
}
).dimensions(this.width / 2 - buttonWidth / 2, y, buttonWidth, buttonHeight).build();
this.addDrawableChild(button);
i++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Done button
this.addDrawableChild(ButtonWidget.builder(
Text.translatable("gui.done"),
button -> this.client.setScreen(parent)
).dimensions(this.width / 2 - 75, startY + i * (buttonHeight + spacing) + 10, 150, 20).build());
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
context.fill(0, 0, this.width, this.height, 0xC0101010);
int titleX = (this.width / 2) - (this.textRenderer.getWidth(this.title) / 2);
context.drawTextWithShadow(this.textRenderer, this.title, titleX, 20, 0xFFFFFF);
super.render(context, mouseX, mouseY, delta);
}
private String formatFieldName(String rawName) {
StringBuilder result = new StringBuilder();
char[] chars = rawName.toCharArray();
result.append(Character.toUpperCase(chars[0]));
for (int i = 1; i < chars.length; i++) {
if (Character.isUpperCase(chars[i])) {
result.append(' ');
}
result.append(chars[i]);
}
return result.toString();
}
}

View File

@ -0,0 +1,16 @@
package wtf.hak.survivalfabric.modmenu;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class ModMenuIntegration implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
System.out.println("Does ModMenuIntegration even load?");
return parent -> new ConfigScreen(parent);
}
}

View File

@ -0,0 +1,12 @@
{
"required": true,
"package": "wtf.hak.survivalfabric.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"BackgroundRendererMixin",
"EntityMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -38,6 +38,8 @@ public class Config {
public boolean veinMinerEnabled = true; public boolean veinMinerEnabled = true;
public int maxVeinSize = 99999; public int maxVeinSize = 99999;
public boolean chatCalcEnabled = true;
public ScreenHandlerType<GenericContainerScreenHandler> screenHandlerType() { public ScreenHandlerType<GenericContainerScreenHandler> screenHandlerType() {
return switch (sharedEnderChestRows) { return switch (sharedEnderChestRows) {
case 1 -> ScreenHandlerType.GENERIC_9X1; case 1 -> ScreenHandlerType.GENERIC_9X1;

View File

@ -1,8 +1,10 @@
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;
@ -16,6 +18,8 @@ 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.Objects;
import java.util.Set; import java.util.Set;
@ -24,7 +28,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) { 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);
} }
@ -51,9 +55,74 @@ public abstract class PlayerManagerMixin {
@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 && ConfigManager.getConfig().chatMessageEnabled) {
Text text = Text.literal(String.format(ConfigManager.getConfig().chatMessage, sender.getName().getString(), message.getContent().getString())); String rawMessage = message.getContent().getString().trim();
Objects.requireNonNull(sender.getServer()).getPlayerManager().broadcast(text, false); if(sender != null && ConfigManager.getConfig().chatCalcEnabled && 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;
} catch (Exception e) {}
}
Text text = Text.literal(String.format(ConfigManager.getConfig().chatMessage, sender.getName().getString(), rawMessage));
sender.getServer().getPlayerManager().broadcast(text, 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) {
return evaluate(expression.replaceAll("\\s", ""), new int[]{0});
}
private double evaluate(String expr, int[] index) {
double value = parseTerm(expr, index);
while (index[0] < expr.length()) {
char op = expr.charAt(index[0]);
if (op != '+' && op != '-') break;
index[0]++;
double nextTerm = parseTerm(expr, index);
value = (op == '+') ? value + nextTerm : value - nextTerm;
}
return value;
}
private double parseTerm(String expr, int[] index) {
double value = parseFactor(expr, index);
while (index[0] < expr.length()) {
char op = expr.charAt(index[0]);
if (op != '*' && op != '/') break;
index[0]++;
double nextFactor = parseFactor(expr, index);
value = (op == '*') ? value * nextFactor : value / nextFactor;
}
return value;
}
private double parseFactor(String expr, int[] index) {
if (expr.charAt(index[0]) == '(') {
index[0]++;
double value = evaluate(expr, index);
index[0]++; // Skip closing ')'
return value;
}
int start = index[0];
while (index[0] < expr.length() && (Character.isDigit(expr.charAt(index[0])) || expr.charAt(index[0]) == '.')) {
index[0]++;
}
return Double.parseDouble(expr.substring(start, index[0]));
}
} }

View File

@ -0,0 +1,19 @@
{
"category.survivalfabric.tpangles": "Teleportation Angles",
"key.survivalfabric.angle0": "-65.19 / -54.23",
"key.survivalfabric.angle1": "-24.86 / -54.23",
"key.survivalfabric.angle2": "-65.02 / -41.68",
"key.survivalfabric.angle3": "-25.03 / -41.71",
"key.survivalfabric.angle4": "24.81 / -54.23",
"key.survivalfabric.angle5": "65.14 / -54.23",
"key.survivalfabric.angle6": "24.98 / -41.68",
"key.survivalfabric.angle7": "64.97 / -41.71",
"key.survivalfabric.angle8": "114.81 / -54.23",
"key.survivalfabric.angle9": "155.14 / -54.23",
"key.survivalfabric.angle10": "114.98 / -41.68",
"key.survivalfabric.angle11": "154.97 / -41.71",
"key.survivalfabric.angle12": "204.81 / -54.23",
"key.survivalfabric.angle13": "245.14 / -54.23",
"key.survivalfabric.angle14": "204.98 / -41.68",
"key.survivalfabric.angle15": "244.97 / -41.71"
}

View File

@ -9,6 +9,6 @@
"#minecraft:coal_ores", "#minecraft:coal_ores",
"#minecraft:copper_ores", "#minecraft:copper_ores",
"minecraft:ancient_debris", "minecraft:ancient_debris",
"minecraft:magma_block" "minecraft:nether_quartz_ore"
] ]
} }

View File

@ -9,7 +9,8 @@
], ],
"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"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
"icon": "assets/survivalfabric/icon.png", "icon": "assets/survivalfabric/icon.png",
@ -18,12 +19,22 @@
"main": [ "main": [
"wtf.hak.survivalfabric.SurvivalFabric" "wtf.hak.survivalfabric.SurvivalFabric"
], ],
"client": [
"wtf.hak.survivalfabric.SurvivalFabricClient"
],
"fabric-datagen": [ "fabric-datagen": [
"wtf.hak.survivalfabric.SurvivalFabricDataGenerator" "wtf.hak.survivalfabric.SurvivalFabricDataGenerator"
],
"modmenu": [
"wtf.hak.survivalfabric.modmenu.ModMenuIntegration"
] ]
}, },
"mixins": [ "mixins": [
"survivalfabric.mixins.json" "survivalfabric.mixins.json",
{
"config": "survivalfabric.client.mixins.json",
"environment": "client"
}
], ],
"depends": { "depends": {
"fabricloader": ">=0.16.10", "fabricloader": ">=0.16.10",
@ -31,7 +42,7 @@
"java": ">=21", "java": ">=21",
"fabric-api": "*" "fabric-api": "*"
}, },
"suggests": { "optional": {
"modmenu": "*"
} }
} }