diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/themixray/repeating/mod/EasyConfig.java new file mode 100644 index 0000000..0a2843a --- /dev/null +++ b/src/main/java/themixray/repeating/mod/EasyConfig.java @@ -0,0 +1,68 @@ +package themixray.repeating.mod; + +import org.json.simple.JSONValue; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.io.File; + +public class EasyConfig { + public final Path path; + public final File file; + public Map data; + + public EasyConfig(Path path, Map def) { + this.path = path; + this.file = path.toFile(); + this.data = new HashMap<>(); + + if (!file.exists()) { + try { + file.createNewFile(); + write(def); + } catch (IOException e) { + e.printStackTrace(); + } + } + reload(); + } + + public EasyConfig(Path path) { + this(path,new HashMap<>()); + } + + public void reload() { + data = read(); + } + + public void save() { + write(data); + } + + private String toJson(Map p) { + return JSONValue.toJSONString(p); + } + + private Map toMap(String j) { + return (Map) JSONValue.parse(j); + } + + private Map read() { + try { + return toMap(Files.readString(path)); + } catch (IOException e) { + e.printStackTrace(); + } + return new HashMap<>(); + } + + private void write(Map p) { + try { + Files.write(path,toJson(p).getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java new file mode 100644 index 0000000..639a68a --- /dev/null +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -0,0 +1,211 @@ +package themixray.repeating.mod; + +import io.wispforest.owo.ui.base.*; +import io.wispforest.owo.ui.component.*; +import io.wispforest.owo.ui.container.*; +import io.wispforest.owo.ui.container.FlowLayout; +import io.wispforest.owo.ui.core.*; +import io.wispforest.owo.ui.core.Insets; +import io.wispforest.owo.util.EventSource; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceType; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; + +public class RepeatingScreen extends BaseOwoScreen { + public RepeatingMod mod; + public ButtonComponent replay_btn; + public ButtonComponent record_btn; + public ButtonComponent loop_btn; + + public RepeatingScreen() { + this.mod = RepeatingMod.me; + } + + @Override + protected @NotNull OwoUIAdapter createAdapter() { + return OwoUIAdapter.create(this, Containers::horizontalFlow); + } + + public void update_btns() { + replay_btn.setMessage(Text.translatable("text.repeating-mod." + + ((mod.is_replaying) ? "stop" : "start")).append(" ") + .append(Text.translatable("text.repeating-mod.replay"))); + record_btn.setMessage(Text.translatable("text.repeating-mod." + + ((mod.is_recording) ? "stop" : "start")).append(" ") + .append(Text.translatable("text.repeating-mod.record"))); + loop_btn.setMessage(Text.of(((mod.loop_replay) ? "\uefff" : "\ueffe"))); + } + + @Override + protected void build(FlowLayout rootComponent) { + rootComponent + .surface(Surface.VANILLA_TRANSLUCENT) + .horizontalAlignment(HorizontalAlignment.CENTER) + .verticalAlignment(VerticalAlignment.CENTER); + + replay_btn = (ButtonComponent) Components.button(Text.of("replay"), + (ButtonComponent btn) -> { + if (!mod.is_recording) { + if (mod.is_replaying) + mod.stopReplay(); + else mod.startReplay(); + update_btns(); + } + }).margins(Insets.of(1)).sizing( + Sizing.fixed(98),Sizing.fixed(20)); + + loop_btn = (ButtonComponent) Components.button(Text.of(""), + (ButtonComponent btn) -> { + mod.loop_replay = !mod.loop_replay; + update_btns(); + }).margins(Insets.of(1)) + .sizing(Sizing.fixed(20),Sizing.fixed(20)); + + record_btn = (ButtonComponent) Components.button(Text.of("record"), + (ButtonComponent btn) -> { + if (!mod.is_replaying) { + if (mod.is_recording) + mod.stopRecording(); + else mod.startRecording(); + update_btns(); + } + }).margins(Insets.of(1)).sizing( + Sizing.fixed(120),Sizing.fixed(20)); + + rootComponent.child( + Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.translatable("text.repeating-mod.basic")).margins(Insets.of(1))) + .padding(Insets.of(5)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1))) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Containers.horizontalFlow(Sizing.content(), Sizing.content()) + .child(replay_btn).child(loop_btn)) + .child(record_btn) + .child(Components.button(Text.translatable( + "text.repeating-mod.export"), + (ButtonComponent btn) -> { + String t = ""; + for (int i = 0; i < mod.record.size(); i++) { + t += mod.record.get(i).toText(); + if (i != mod.record.size()-1) + t += ";"; + } + + File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); + if (!p.exists()) p.mkdir(); + File file = new File(p,"export.txt"); + + try { + if (!file.exists()) file.createNewFile(); + Files.write(file.toPath(), t.getBytes()); + Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\""); + } catch (Exception e) { + e.printStackTrace(); + } + }).margins(Insets.of(10,1,1,1)).sizing( + Sizing.fixed(120),Sizing.fixed(20))) + .child(Components.button(Text.translatable( + "text.repeating-mod.import"), + (ButtonComponent btn) -> { + mod.record.clear(); + + File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); + if (!p.exists()) p.mkdir(); + File file = new File(p,"import.txt"); + + try { + if (!file.exists()) { + file.createNewFile(); + Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\""); + return; + } + String t = Files.readString(file.toPath()); + for (String s:t.split(";")) + mod.record.add(RepeatingMod.RecordEvent.fromText(s)); + } catch (Exception e) { + e.printStackTrace(); + } + }).margins(Insets.of(1)).sizing( + Sizing.fixed(120),Sizing.fixed(20))) + .padding(Insets.of(10)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1))) + /*).child( + Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.translatable("text.repeating-mod.parkour")).margins(Insets.of(1))) + .padding(Insets.of(5)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1))) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.translatable("text.repeating-mod.dev")).margins(Insets.of(1))) + .padding(Insets.of(10)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1)))*/ + ).child( + Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Components.label(Text.translatable("text.repeating-mod.settings")).margins(Insets.of(1))) + .padding(Insets.of(5)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1))) + .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) + .child(Components.discreteSlider(Sizing.fixed(120),0,5) + .decimalPlaces(2) + .setFromDiscreteValue(mod.record_blocks_limit) + .message((String s)->{ + mod.record_blocks_limit = Double.parseDouble(s.replace(",",".")); + mod.conf.data.put("record_blocks_limit",mod.record_blocks_limit); + mod.conf.save(); + return Text.translatable("text.repeating-mod.block_limit",s); + }).scrollStep(0.2) + .margins(Insets.of(1)) + .tooltip(Text.translatable("text.repeating-mod.block_limit_tooltip"))) + .child(Components.discreteSlider(Sizing.fixed(120),0,1000) + .decimalPlaces(0) + .setFromDiscreteValue(mod.record_time_limit) + .message((String s)->{ + mod.record_time_limit = (long) Double.parseDouble(s.replace(",",".")); + mod.conf.data.put("record_time_limit",mod.record_time_limit); + mod.conf.save(); + return Text.translatable("text.repeating-mod.time_limit",s); + }).scrollStep(2) + .margins(Insets.of(1)) + .tooltip(Text.translatable("text.repeating-mod.time_limit_tooltip"))) + .padding(Insets.of(10)) + .surface(Surface.DARK_PANEL) + .verticalAlignment(VerticalAlignment.CENTER) + .horizontalAlignment(HorizontalAlignment.CENTER) + .margins(Insets.of(1))) + ); + update_btns(); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java new file mode 100644 index 0000000..5f3874e --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java @@ -0,0 +1,30 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.input.KeyboardInput; +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 themixray.repeating.mod.RepeatingMod; + +@Mixin(KeyboardInput.class) +public abstract class InputMixin { + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { + if (RepeatingMod.me.is_replaying) { + RepeatingMod.client.player.input.sneaking = RepeatingMod.replay_sneaking; + } + } + + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(boolean slowDown, float f, CallbackInfo ci) { + if (RepeatingMod.me.is_recording) { + RepeatingMod.RecordSneakEvent e = new RepeatingMod. + RecordSneakEvent(RepeatingMod.client.player.input.sneaking); + RepeatingMod.RecordSneakEvent l = ((RepeatingMod.RecordSneakEvent) + RepeatingMod.me.getLastRecord("sneak")); + if (l == null || l.sneaking != e.sneaking) + RepeatingMod.me.recordTick(e); + } + } +} diff --git a/src/main/resources/assets/minecraft/font/default.json b/src/main/resources/assets/minecraft/font/default.json new file mode 100644 index 0000000..6501fb4 --- /dev/null +++ b/src/main/resources/assets/minecraft/font/default.json @@ -0,0 +1,17 @@ +{ + "providers": [ + { + "file":"repeating-mod:ui/no-loop.png", + "chars":["\ueffe"], + "height":16, + "ascent":12, + "type":"bitmap" + },{ + "file":"repeating-mod:ui/loop.png", + "chars":["\uefff"], + "height":16, + "ascent":12, + "type":"bitmap" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/repeating-mod/textures/ui/loop.png b/src/main/resources/assets/repeating-mod/textures/ui/loop.png new file mode 100644 index 0000000..706aff4 Binary files /dev/null and b/src/main/resources/assets/repeating-mod/textures/ui/loop.png differ diff --git a/src/main/resources/assets/repeating-mod/textures/ui/no-loop.png b/src/main/resources/assets/repeating-mod/textures/ui/no-loop.png new file mode 100644 index 0000000..b50d39f Binary files /dev/null and b/src/main/resources/assets/repeating-mod/textures/ui/no-loop.png differ diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 0000000..f0987b0 Binary files /dev/null and b/src/main/resources/icon.png differ