From 96c4ce4b9d3b03166491418d93b3ded906e4f8b5 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 30 May 2023 22:58:25 +0300 Subject: [PATCH 01/72] added move tp when repeating is wrong --- gradle.properties | 2 +- .../themixray/repeating/mod/EasyConfig.java | 7 ++++ .../themixray/repeating/mod/RepeatingMod.java | 30 ++++++++++++----- .../repeating/mod/RepeatingScreen.java | 32 +++++++------------ .../assets/repeating-mod/lang/en_us.json | 7 ++-- .../assets/repeating-mod/lang/ru_ru.json | 7 ++-- 6 files changed, 47 insertions(+), 38 deletions(-) diff --git a/gradle.properties b/gradle.properties index 47df628..bcc5a6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.19.3+build.1 loader_version=0.14.17 # Mod Properties -mod_version = 1.0.2 +mod_version = 1.0.3 maven_group = themixray.repeating.mod archives_base_name = repeating-mod diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/themixray/repeating/mod/EasyConfig.java index a2c6ae1..05ba00e 100644 --- a/src/main/java/themixray/repeating/mod/EasyConfig.java +++ b/src/main/java/themixray/repeating/mod/EasyConfig.java @@ -29,7 +29,14 @@ public class EasyConfig { e.printStackTrace(); } } + reload(); + + for (Map.Entry m:def.entrySet()) + if (!data.containsKey(m.getKey())) + data.put(m.getKey(),m.getValue()); + + save(); } public EasyConfig(Path f, Map def) { this(f.toFile(),def); diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 88c48d2..2f16bc2 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -28,6 +28,7 @@ public class RepeatingMod implements ClientModInitializer { public static final FabricLoader loader = FabricLoader.getInstance(); public static RepeatingMod me; + public Thread move_tick = null; public List record = new ArrayList<>(); public boolean is_recording = false; public Date last_record = null; @@ -42,8 +43,7 @@ public class RepeatingMod implements ClientModInitializer { private static KeyBinding toggle_replay_key; private static KeyBinding toggle_record_key; - public double record_blocks_limit = 2; - public long record_time_limit = 50; + public long record_pos_delay = 1000; public EasyConfig conf; @@ -53,12 +53,11 @@ public class RepeatingMod implements ClientModInitializer { me = this; Map def = new HashMap<>(); - def.put("record_blocks_limit", record_blocks_limit); - def.put("record_time_limit", record_time_limit); - conf = new EasyConfig(new File(loader.getConfigDir().toFile(),"repeating-mod.yml").toPath(),def); + def.put("record_pos_delay", (int) record_pos_delay); - record_blocks_limit = (double) conf.data.get("record_blocks_limit"); - record_time_limit = (Integer) conf.data.get("record_time_limit"); + conf = new EasyConfig(loader.getConfigDir(),"repeating-mod.yml",def); + + record_pos_delay = (Integer) conf.data.get("record_pos_delay"); menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( "key.repeating-mod.menu",InputUtil.Type.KEYSYM, @@ -108,7 +107,20 @@ public class RepeatingMod implements ClientModInitializer { is_recording = true; menu.update_btns(); record.clear(); - record.add(new RecordMoveEvent(client.player.getPos(),client.player.getHeadYaw(),client.player.getPitch())); + + move_tick = new Thread(() -> { + while (is_recording) { + record.add(new RecordMoveEvent(client.player.getPos(), + client.player.getHeadYaw(),client.player.getPitch())); + try { + Thread.sleep(record_pos_delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + move_tick.start(); + sendMessage(Text.translatable("message.repeating-mod.record_start")); } @@ -200,6 +212,7 @@ public class RepeatingMod implements ClientModInitializer { public void stopRecording() { is_recording = false; + move_tick = null; menu.update_btns(); last_record = null; sendMessage(Text.translatable("message.repeating-mod.record_stop")); @@ -228,6 +241,7 @@ public class RepeatingMod implements ClientModInitializer { public void stopReplay() { is_recording = false; is_replaying = false; + replay = null; menu.update_btns(); sendMessage(Text.translatable("message.repeating-mod.replay_stop")); } diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 871b540..0a7ec2b 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -155,7 +155,7 @@ public class RepeatingScreen extends BaseOwoScreen { .verticalAlignment(VerticalAlignment.CENTER) .horizontalAlignment(HorizontalAlignment.CENTER) .margins(Insets.of(1)))*/ - /*).child( + ).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))) @@ -165,34 +165,24 @@ public class RepeatingScreen extends BaseOwoScreen { .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) + .child(Components.discreteSlider(Sizing.fixed(120),-24,5000) + .setFromDiscreteValue(mod.record_pos_delay) .message((String s)->{ - mod.record_blocks_limit = Double.parseDouble(s.replace(",",".")); - mod.conf.data.put("record_blocks_limit",mod.record_blocks_limit); + mod.record_pos_delay = Long.parseLong(s); + mod.conf.data.put("record_pos_delay",mod.record_pos_delay); mod.conf.save(); - return Text.translatable("text.repeating-mod.block_limit",s); - }).scrollStep(0.2) + if (mod.record_pos_delay > -1) + return Text.translatable("text.repeating-mod.pos_delay", s); + return Text.translatable("text.repeating-mod.nan_pos_delay", s); + }).scrollStep(25) .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"))) + .tooltip(Text.translatable("text.repeating-mod.pos_delay_text"))) .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/resources/assets/repeating-mod/lang/en_us.json b/src/main/resources/assets/repeating-mod/lang/en_us.json index 57f79f0..d9417ba 100644 --- a/src/main/resources/assets/repeating-mod/lang/en_us.json +++ b/src/main/resources/assets/repeating-mod/lang/en_us.json @@ -14,10 +14,9 @@ "text.repeating-mod.parkour": "Parkour mode", "text.repeating-mod.settings": "Settings", "text.repeating-mod.dev": "In development...", - "text.repeating-mod.block_limit": "Block limit: %s", - "text.repeating-mod.block_limit_tooltip": "Two recording events will\nbe summed up if the\ndistance between them is\nless than the limit.", - "text.repeating-mod.time_limit": "Time limit: %s ms", - "text.repeating-mod.time_limit_tooltip": "Two recording events will\nbe summed up if the time\nbetween them is less than\nthe limit.", + "text.repeating-mod.nan_pos_delay": "No pos delay", + "text.repeating-mod.pos_delay": "Pos delay: %s ms", + "text.repeating-mod.pos_delay_text": "Delay after which the pos event is added", "message.repeating-mod.replay_start": "Replay started", "message.repeating-mod.replay_stop": "Replay finished", diff --git a/src/main/resources/assets/repeating-mod/lang/ru_ru.json b/src/main/resources/assets/repeating-mod/lang/ru_ru.json index 8b62f15..ed68292 100644 --- a/src/main/resources/assets/repeating-mod/lang/ru_ru.json +++ b/src/main/resources/assets/repeating-mod/lang/ru_ru.json @@ -14,10 +14,9 @@ "text.repeating-mod.parkour": "Режим паркура", "text.repeating-mod.settings": "Настройки", "text.repeating-mod.dev": "В разработке...", - "text.repeating-mod.block_limit": "Лимит блоков: %s", - "text.repeating-mod.block_limit_tooltip": "Два ивента записи будут\nсуммироваться, если\nрасстояние между ними\nменьше лимита.", - "text.repeating-mod.time_limit": "Лимит времени: %s мс", - "text.repeating-mod.time_limit_tooltip": "Два ивента записи будут\nсуммироваться, если время\nмежду ними меньше лимита.", + "text.repeating-mod.nan_pos_delay": "Задержки позиции нету", + "text.repeating-mod.pos_delay": "Задержка позиции: %s мс", + "text.repeating-mod.pos_delay_text": "Задержка, после которой добавляется ивент позиции", "message.repeating-mod.replay_start": "Повтор начат", "message.repeating-mod.replay_stop": "Повтор закончен", From 629a44a27ad39d85c5c47cf7ffc18e4d1433c087 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Wed, 31 May 2023 00:38:00 +0300 Subject: [PATCH 02/72] fix -1 pos delay --- .../themixray/repeating/mod/RepeatingMod.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 2f16bc2..050b843 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -108,18 +108,20 @@ public class RepeatingMod implements ClientModInitializer { menu.update_btns(); record.clear(); - move_tick = new Thread(() -> { - while (is_recording) { - record.add(new RecordMoveEvent(client.player.getPos(), - client.player.getHeadYaw(),client.player.getPitch())); - try { - Thread.sleep(record_pos_delay); - } catch (InterruptedException e) { - e.printStackTrace(); + if (record_pos_delay != -1) { + move_tick = new Thread(() -> { + while (is_recording) { + record.add(new RecordMoveEvent(client.player.getPos(), + client.player.getHeadYaw(), client.player.getPitch())); + try { + Thread.sleep(record_pos_delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - } - }); - move_tick.start(); + }); + move_tick.start(); + } sendMessage(Text.translatable("message.repeating-mod.record_start")); } From 51b68dd48f2e52b9541ba6ec7b7d1978f17ba3fc Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Wed, 31 May 2023 12:22:28 +0300 Subject: [PATCH 03/72] fix easy config --- build.gradle | 1 - gradle.properties | 2 +- .../themixray/repeating/mod/EasyConfig.java | 40 ++--- .../themixray/repeating/mod/RepeatingMod.java | 139 +++++++++++------- .../repeating/mod/RepeatingScreen.java | 20 ++- .../repeating/mod/mixin/MovementMixin.java | 15 ++ 6 files changed, 135 insertions(+), 82 deletions(-) diff --git a/build.gradle b/build.gradle index 0efd2d3..fb32d3f 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,6 @@ dependencies { // include this if you don't want force your users to install owo // sentinel will warn them and give the option to download it automatically include "io.wispforest:owo-sentinel:${project.owo_version}" - modImplementation 'org.yaml:snakeyaml:2.0' } base { diff --git a/gradle.properties b/gradle.properties index bcc5a6b..8a30063 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.19.3+build.1 loader_version=0.14.17 # Mod Properties -mod_version = 1.0.3 +mod_version = 1.0.4 maven_group = themixray.repeating.mod archives_base_name = repeating-mod diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/themixray/repeating/mod/EasyConfig.java index 05ba00e..eab45d1 100644 --- a/src/main/java/themixray/repeating/mod/EasyConfig.java +++ b/src/main/java/themixray/repeating/mod/EasyConfig.java @@ -1,7 +1,5 @@ package themixray.repeating.mod; -import org.yaml.snakeyaml.Yaml; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -12,14 +10,12 @@ import java.io.File; public class EasyConfig { public final Path path; public final File file; - public Map data; - private Yaml yaml; + public Map data; - public EasyConfig(File f, Map def) { + public EasyConfig(File f, Map def) { this.path = f.toPath(); this.file = f; this.data = new HashMap<>(); - this.yaml = new Yaml(); if (!file.exists()) { try { @@ -32,22 +28,22 @@ public class EasyConfig { reload(); - for (Map.Entry m:def.entrySet()) + for (Map.Entry m:def.entrySet()) if (!data.containsKey(m.getKey())) data.put(m.getKey(),m.getValue()); save(); } - public EasyConfig(Path f, Map def) { + public EasyConfig(Path f, Map def) { this(f.toFile(),def); } - public EasyConfig(String parent,String child,Map def) { + public EasyConfig(String parent,String child,Map def) { this(new File(parent,child),def); } - public EasyConfig(File parent,String child,Map def) { + public EasyConfig(File parent,String child,Map def) { this(new File(parent,child),def); } - public EasyConfig(Path parent,String child,Map def) { + public EasyConfig(Path parent,String child,Map def) { this(new File(parent.toFile(),child),def); } @@ -74,14 +70,22 @@ public class EasyConfig { write(data); } - private String toYaml(Map p) { - return yaml.dump(p); + private String toText(Map p) { + String t = ""; + for (Map.Entry e:p.entrySet()) + t += e.getKey() + "=" + e.getValue() + "\n"; + return t; } - private Map toMap(String j) { - return (Map) yaml.load(j); + private Map toMap(String j) { + Map m = new HashMap<>(); + for (String l:j.split("\n")) { + String s[] = l.split("="); + m.put(s[0],s[1]); + } + return m; } - private Map read() { + private Map read() { try { return toMap(Files.readString(path)); } catch (IOException e) { @@ -89,9 +93,9 @@ public class EasyConfig { } return new HashMap<>(); } - private void write(Map p) { + private void write(Map p) { try { - Files.write(path, toYaml(p).getBytes()); + Files.write(path, toText(p).getBytes()); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 050b843..0c92ab7 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -52,12 +52,12 @@ public class RepeatingMod implements ClientModInitializer { LOGGER.info("Repeating mod initialized"); me = this; - Map def = new HashMap<>(); - def.put("record_pos_delay", (int) record_pos_delay); + Map def = new HashMap<>(); + def.put("record_pos_delay", String.valueOf(record_pos_delay)); - conf = new EasyConfig(loader.getConfigDir(),"repeating-mod.yml",def); + conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); - record_pos_delay = (Integer) conf.data.get("record_pos_delay"); + record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( "key.repeating-mod.menu",InputUtil.Type.KEYSYM, @@ -71,9 +71,8 @@ public class RepeatingMod implements ClientModInitializer { menu = new RepeatingScreen(); ClientTickEvents.END_CLIENT_TICK.register(client -> { - if (menu_key.wasPressed()) { + if (menu_key.wasPressed()) client.setScreen(menu); - } if (toggle_replay_key.wasPressed()) { if (!is_recording) { if (is_replaying) @@ -108,16 +107,19 @@ public class RepeatingMod implements ClientModInitializer { menu.update_btns(); record.clear(); - if (record_pos_delay != -1) { + record.add(new RecordMoveEvent(client.player.getPos(), + client.player.getHeadYaw(), client.player.getPitch())); + + if (record_pos_delay > 0) { move_tick = new Thread(() -> { while (is_recording) { - record.add(new RecordMoveEvent(client.player.getPos(), - client.player.getHeadYaw(), client.player.getPitch())); try { Thread.sleep(record_pos_delay); } catch (InterruptedException e) { e.printStackTrace(); } + record.add(new RecordMoveEvent(client.player.getPos(), + client.player.getHeadYaw(), client.player.getPitch())); } }); move_tick.start(); @@ -152,7 +154,8 @@ public class RepeatingMod implements ClientModInitializer { client.player.getBodyYaw(), client.player.getPitch(), client.player.isSprinting(), - client.player.getYaw()); + client.player.getYaw(), + client.player.getMovementSpeed()); recordTick(e); } else { RecordInputEvent e = new RecordInputEvent( @@ -166,7 +169,7 @@ public class RepeatingMod implements ClientModInitializer { ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(), ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), - client.player.getYaw()); + client.player.getYaw(),client.player.getMovementSpeed()); if (!(e.isEmpty() && e.yaw == l.yaw && @@ -195,12 +198,14 @@ public class RepeatingMod implements ClientModInitializer { client.player.getBodyYaw(), client.player.getPitch(), client.player.isSprinting(), - client.player.getYaw()); + client.player.getYaw(), + client.player.getMovementSpeed()); recordTick(e); } else { - RecordInputEvent e = new RecordInputEvent(null,null,null,null, - null,null,null,null,client.player.getHeadYaw(), - RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(),null,client.player.getYaw()); + RecordInputEvent e = new RecordInputEvent(null,null,null, + null,null,null,null,null, + client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(), + null,client.player.getYaw(),client.player.getMovementSpeed()); if (!(e.yaw == l.yaw && e.head_yaw == l.head_yaw && @@ -270,47 +275,15 @@ public class RepeatingMod implements ClientModInitializer { String type = String.valueOf(t.charAt(0)); String[] args = t.substring(2).split("&"); if (type.equals("d")) { - return new RecordDelayEvent( - Long.parseLong(args[0])); + return RecordDelayEvent.fromArgs(args); } else if (type.equals("m")) { - return new RecordMoveEvent(new Vec3d( - Double.parseDouble(args[0]), - Double.parseDouble(args[1]), - Double.parseDouble(args[2])), - Float.parseFloat(args[3]), - Float.parseFloat(args[4])); + return RecordMoveEvent.fromArgs(args); } else if (type.equals("p")) { - return new RecordInputEvent( - (args[0].equals("n")?null:args[0].equals("1")), - (args[1].equals("n")?null:args[1].equals("1")), - (args[2].equals("n")?null:Float.parseFloat(args[2])), - (args[3].equals("n")?null:Float.parseFloat(args[3])), - (args[4].equals("n")?null:args[4].equals("1")), - (args[5].equals("n")?null:args[5].equals("1")), - (args[6].equals("n")?null:args[6].equals("1")), - (args[7].equals("n")?null:args[7].equals("1")), - Float.parseFloat(args[8]),Float.parseFloat(args[9]), - Float.parseFloat(args[10]), - (args[11].equals("n")?null:args[11].equals("1")), - Float.parseFloat(args[12])); + return RecordInputEvent.fromArgs(args); } else if (type.equals("b")) { - return new RecordBlockBreakEvent(new BlockPos( - Integer.parseInt(args[0]), - Integer.parseInt(args[1]), - Integer.parseInt(args[2]))); + return RecordBlockBreakEvent.fromArgs(args); } else if (type.equals("i")) { - return new RecordBlockInteractEvent( - Hand.valueOf(args[5]), - new BlockHitResult(new Vec3d( - Double.parseDouble(args[0]), - Double.parseDouble(args[1]), - Double.parseDouble(args[2])), - Direction.byId(Integer.parseInt(args[4])), - new BlockPos( - Integer.parseInt(args[0]), - Integer.parseInt(args[1]), - Integer.parseInt(args[2])), - args[3].equals("1"))); + return RecordBlockInteractEvent.fromArgs(args); } } catch (Exception e) { e.printStackTrace(); @@ -322,6 +295,10 @@ public class RepeatingMod implements ClientModInitializer { public static class RecordDelayEvent extends RecordEvent { public long delay; + public static RecordDelayEvent fromArgs(String[] a) { + return new RecordDelayEvent(Long.parseLong(a[0])); + } + public RecordDelayEvent(long delay) { this.delay = delay; } @@ -347,6 +324,15 @@ public class RepeatingMod implements ClientModInitializer { public float yaw; public float pitch; + public static RecordMoveEvent fromArgs(String[] a) { + return new RecordMoveEvent(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Float.parseFloat(a[3]), + Float.parseFloat(a[4])); + } + public RecordMoveEvent(Vec3d vec,float yaw,float pitch) { this.vec = vec; this.yaw = yaw; @@ -385,7 +371,24 @@ public class RepeatingMod implements ClientModInitializer { public float head_yaw; public float body_yaw; public float pitch; + public float speed; + public static RecordInputEvent fromArgs(String[] a) { + return new RecordInputEvent( + (a[0].equals("n")?null:a[0].equals("1")), + (a[1].equals("n")?null:a[1].equals("1")), + (a[2].equals("n")?null:Float.parseFloat(a[2])), + (a[3].equals("n")?null:Float.parseFloat(a[3])), + (a[4].equals("n")?null:a[4].equals("1")), + (a[5].equals("n")?null:a[5].equals("1")), + (a[6].equals("n")?null:a[6].equals("1")), + (a[7].equals("n")?null:a[7].equals("1")), + Float.parseFloat(a[8]),Float.parseFloat(a[9]), + Float.parseFloat(a[10]), + (a[11].equals("n")?null:a[11].equals("1")), + Float.parseFloat(a[12]), + Float.parseFloat(a[13])); + } public RecordInputEvent(Boolean sneaking, Boolean jumping, @@ -399,7 +402,8 @@ public class RepeatingMod implements ClientModInitializer { float body_yaw, float head_pitch, Boolean sprinting, - float yaw) { + float yaw, + float speed) { this.sneaking = sneaking; this.jumping = jumping; this.movementSideways = movementSideways; @@ -413,6 +417,7 @@ public class RepeatingMod implements ClientModInitializer { this.pitch = head_pitch; this.sprinting = sprinting; this.yaw = yaw; + this.speed = speed; } public void fillEmpty(RecordInputEvent e) { @@ -454,6 +459,8 @@ public class RepeatingMod implements ClientModInitializer { client.player.setBodyYaw(body_yaw); if (client.player.getPitch() != pitch) client.player.setPitch(pitch); + if (client.player.getMovementSpeed() != speed) + client.player.setMovementSpeed(speed); if (sneaking != null && client.player.input.sneaking != sneaking) client.player.input.sneaking = sneaking; if (jumping != null && client.player.input.jumping != jumping) @@ -483,8 +490,10 @@ public class RepeatingMod implements ClientModInitializer { ((pressingLeft==null)?"n":(pressingLeft?"1":"0"))+"&"+ ((pressingRight==null)?"n":(pressingRight?"1":"0"))+"&"+ head_yaw+"&"+body_yaw+"&"+ pitch +"&"+ - ((sprinting==null)?"n":(sprinting?"1":"0")+"&"+ yaw); + ((sprinting==null)?"n":(sprinting?"1":"0")+ + "&"+yaw+"&"+speed); } + public String getType() { return "input"; } @@ -493,6 +502,13 @@ public class RepeatingMod implements ClientModInitializer { public static class RecordBlockBreakEvent extends RecordEvent { public BlockPos pos; + public static RecordBlockBreakEvent fromArgs(String[] a) { + return new RecordBlockBreakEvent(new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2]))); + } + public RecordBlockBreakEvent( BlockPos pos) { this.pos = pos; @@ -514,6 +530,21 @@ public class RepeatingMod implements ClientModInitializer { public Hand hand; public BlockHitResult hitResult; + public static RecordBlockInteractEvent fromArgs(String[] a) { + return new RecordBlockInteractEvent( + Hand.valueOf(a[5]), + new BlockHitResult(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Direction.byId(Integer.parseInt(a[4])), + new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2])), + a[3].equals("1"))); + } + public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { this.hand = hand; this.hitResult = hitResult; diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 0a7ec2b..4e57b8d 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -18,6 +18,7 @@ public class RepeatingScreen extends BaseOwoScreen { public ButtonComponent replay_btn; public ButtonComponent record_btn; public ButtonComponent loop_btn; + public boolean was_build = false; public RepeatingScreen() { this.mod = RepeatingMod.me; @@ -29,13 +30,15 @@ public class RepeatingScreen extends BaseOwoScreen { } 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 "))); + if (was_build) { + 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 @@ -73,6 +76,7 @@ public class RepeatingScreen extends BaseOwoScreen { } }).margins(Insets.of(1)).sizing( Sizing.fixed(120),Sizing.fixed(20)); + was_build = true; rootComponent.child( Containers.horizontalFlow(Sizing.content(), Sizing.content()).child( @@ -169,7 +173,7 @@ public class RepeatingScreen extends BaseOwoScreen { .setFromDiscreteValue(mod.record_pos_delay) .message((String s)->{ mod.record_pos_delay = Long.parseLong(s); - mod.conf.data.put("record_pos_delay",mod.record_pos_delay); + mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); mod.conf.save(); if (mod.record_pos_delay > -1) return Text.translatable("text.repeating-mod.pos_delay", s); diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index cc0e1fa..a88da8c 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -10,6 +10,7 @@ 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; +import themixray.repeating.mod.TickTask; @Mixin(ClientPlayerEntity.class) public abstract class MovementMixin { @@ -29,6 +30,20 @@ public abstract class MovementMixin { }); } + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + for (TickTask t:TickTask.ticks) + if (t.getAt() == TickTask.TickAt.HEAD) + t.tick(); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + for (TickTask t:TickTask.ticks) + if (t.getAt() == TickTask.TickAt.TAIL) + t.tick(); + } + @Inject(at = @At(value = "HEAD"), method = "tickMovement") private void onMove(CallbackInfo ci) { if (RepeatingMod.me.is_recording) { From 7dfbbdc959685d4f3b932f26c19f8e9a8809cbc5 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Wed, 31 May 2023 22:42:20 +0300 Subject: [PATCH 04/72] ms -> ticks --- .../themixray/repeating/mod/RepeatingMod.java | 98 ++++++++++++------- .../repeating/mod/RepeatingScreen.java | 4 +- .../themixray/repeating/mod/TickTask.java | 94 ++++++++++++++++++ .../repeating/mod/mixin/ClientMixin.java | 24 +++++ .../repeating/mod/mixin/EntityMixin.java | 36 +++++++ .../repeating/mod/mixin/MovementMixin.java | 35 ++----- .../repeating/mod/mixin/RendererMixin.java | 12 ++- .../assets/repeating-mod/lang/en_us.json | 6 +- .../assets/repeating-mod/lang/ru_ru.json | 6 +- src/main/resources/fabric.mod.json | 2 +- src/main/resources/repeating-mod.mixins.json | 5 +- 11 files changed, 245 insertions(+), 77 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/TickTask.java create mode 100644 src/main/java/themixray/repeating/mod/mixin/ClientMixin.java create mode 100644 src/main/java/themixray/repeating/mod/mixin/EntityMixin.java diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 0c92ab7..4ed30dd 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -19,7 +19,6 @@ import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.util.*; public class RepeatingMod implements ClientModInitializer { @@ -28,22 +27,24 @@ public class RepeatingMod implements ClientModInitializer { public static final FabricLoader loader = FabricLoader.getInstance(); public static RepeatingMod me; - public Thread move_tick = null; public List record = new ArrayList<>(); public boolean is_recording = false; - public Date last_record = null; + public long last_record = -1; + public TickTask move_tick = null; - public Thread replay = null; + public TickTask replay_tick = null; public boolean is_replaying = false; public boolean loop_replay = false; public static RecordInputEvent input_replay = null; + public long living_ticks = 0; + public static RepeatingScreen menu; private static KeyBinding menu_key; private static KeyBinding toggle_replay_key; private static KeyBinding toggle_record_key; - public long record_pos_delay = 1000; + public long record_pos_delay = 20; public EasyConfig conf; @@ -90,6 +91,13 @@ public class RepeatingMod implements ClientModInitializer { } } }); + + new TickTask(0,0) { + @Override + public void run() { + living_ticks++; + } + }; } public RecordEvent getLastRecord(String t) { @@ -111,31 +119,30 @@ public class RepeatingMod implements ClientModInitializer { client.player.getHeadYaw(), client.player.getPitch())); if (record_pos_delay > 0) { - move_tick = new Thread(() -> { - while (is_recording) { - try { - Thread.sleep(record_pos_delay); - } catch (InterruptedException e) { - e.printStackTrace(); - } + move_tick = new TickTask( + record_pos_delay, + record_pos_delay) { + @Override + public void run() { record.add(new RecordMoveEvent(client.player.getPos(), client.player.getHeadYaw(), client.player.getPitch())); } - }); - move_tick.start(); + }; } sendMessage(Text.translatable("message.repeating-mod.record_start")); } public void recordTick(RecordEvent e) { - Date now = new Date(); - if (last_record != null) { - long diff = now.getTime() - last_record.getTime(); - if (diff >= 0) record.add(new RecordDelayEvent(diff)); + if (is_recording) { + long now = living_ticks; + if (last_record != -1) { + long diff = now - last_record - 2; + if (diff > 0) record.add(new RecordDelayEvent(diff)); + } + record.add(e); + last_record = now; } - record.add(e); - last_record = now; } public void recordAllInput() { @@ -219,9 +226,12 @@ public class RepeatingMod implements ClientModInitializer { public void stopRecording() { is_recording = false; - move_tick = null; + if (move_tick != null) { + move_tick.cancel(); + move_tick = null; + } menu.update_btns(); - last_record = null; + last_record = -1; sendMessage(Text.translatable("message.repeating-mod.record_stop")); } @@ -230,25 +240,41 @@ public class RepeatingMod implements ClientModInitializer { is_recording = false; is_replaying = true; menu.update_btns(); - replay = new Thread(() -> { - while (true) { - for (RecordEvent e : record) { - if (is_replaying) { - e.callback(); - } + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { + public int replay_index = 0; + + @Override + public void run() { + if (!is_replaying) cancel(); + RecordEvent e = record.get(replay_index); + if (e instanceof RecordDelayEvent) { + setDelay(((RecordDelayEvent) e).delay); + } else { + e.callback(); + } + + replay_index++; + if (!loop_replay) { + if (replay_index == record.size()) { + stopReplay(); + cancel(); + } + } else if (replay_index == record.size()) { + replay_index = 0; } - if (!loop_replay || !is_replaying) break; } - stopReplay(); - }); - replay.start(); + }; + sendMessage(Text.translatable("message.repeating-mod.replay_start")); } public void stopReplay() { is_recording = false; is_replaying = false; - replay = null; + if (replay_tick != null) { + replay_tick.cancel(); + replay_tick = null; + } menu.update_btns(); sendMessage(Text.translatable("message.repeating-mod.replay_stop")); } @@ -265,6 +291,10 @@ public class RepeatingMod implements ClientModInitializer { .append("] ").append(text)); } + public static void sendDebug(String s) { + client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); + } + public static abstract class RecordEvent { abstract void callback(); abstract String toText(); @@ -305,7 +335,7 @@ public class RepeatingMod implements ClientModInitializer { public void callback() { try { - Thread.sleep(delay); + Thread.sleep(delay/20*1000); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 4e57b8d..5fe187e 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -169,7 +169,7 @@ public class RepeatingScreen extends BaseOwoScreen { .horizontalAlignment(HorizontalAlignment.CENTER) .margins(Insets.of(1))) .child(Containers.verticalFlow(Sizing.content(), Sizing.content()) - .child(Components.discreteSlider(Sizing.fixed(120),-24,5000) + .child(Components.discreteSlider(Sizing.fixed(120),-20,100) .setFromDiscreteValue(mod.record_pos_delay) .message((String s)->{ mod.record_pos_delay = Long.parseLong(s); @@ -177,7 +177,7 @@ public class RepeatingScreen extends BaseOwoScreen { mod.conf.save(); if (mod.record_pos_delay > -1) return Text.translatable("text.repeating-mod.pos_delay", s); - return Text.translatable("text.repeating-mod.nan_pos_delay", s); + return Text.translatable("text.repeating-mod.nan_pos_delay"); }).scrollStep(25) .margins(Insets.of(1)) .tooltip(Text.translatable("text.repeating-mod.pos_delay_text"))) diff --git a/src/main/java/themixray/repeating/mod/TickTask.java b/src/main/java/themixray/repeating/mod/TickTask.java new file mode 100644 index 0000000..f3beed1 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/TickTask.java @@ -0,0 +1,94 @@ +package themixray.repeating.mod; + +import java.util.ArrayList; +import java.util.List; + +public abstract class TickTask implements Runnable { + public static List tasks = new ArrayList<>(); + + public static void tickTasks(TickAt at) { + for (TickTask t:new ArrayList<>(tasks)) + if (t.getAt() == at) t.tick(); + } + + private long living; + private long delay; + + private boolean is_repeating; + private long period; + + private boolean is_cancelled; + private TickAt at; + + public enum TickAt { + CLIENT_HEAD, CLIENT_TAIL, + MOVEMENT_HEAD, MOVEMENT_TAIL, + RENDER_HEAD, RENDER_TAIL + } + + public TickTask(long delay, TickAt at) { + this.is_cancelled = false; + this.is_repeating = false; + this.delay = delay; + this.living = 0; + this.period = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay, long period, TickAt at) { + this.is_cancelled = false; + this.is_repeating = true; + this.delay = delay; + this.period = period; + this.living = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay) { + this(delay,TickAt.CLIENT_HEAD); + } + + public TickTask(long delay, long period) { + this(delay,period,TickAt.CLIENT_HEAD); + } + + public void cancel() { + if (!is_cancelled) { + is_cancelled = true; + tasks.remove(this); + } + } + + public boolean isCancelled() { + return is_cancelled; + } + + public TickAt getAt() { + return at; + } + + public void setDelay(long delay) { + if (is_repeating) { + this.delay = delay; + } + } + public long getDelay() { + return this.delay; + } + + public void tick() { + if (living >= delay) { + if (is_repeating) { + delay = period; + run(); + living = -1; + } else { + run(); + cancel(); + } + } + living++; + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java new file mode 100644 index 0000000..1d077ac --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java @@ -0,0 +1,24 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.MinecraftClient; +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; +import themixray.repeating.mod.TickTask; + +@Mixin(MinecraftClient.class) +public abstract class ClientMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + if (RepeatingMod.me.is_recording) + RepeatingMod.me.recordAllInput(); + TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java new file mode 100644 index 0000000..8b09ad0 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java @@ -0,0 +1,36 @@ +package themixray.repeating.mod.mixin; + +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.Entity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.HitResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.TickTask; + +import java.util.UUID; + +@Mixin(Entity.class) +public abstract class EntityMixin { + @Shadow public abstract UUID getUuid(); + + @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) + private void onSprint(boolean sprinting,CallbackInfo ci) { + if (getUuid().equals(RepeatingMod.client.player.getUuid())) { + if (RepeatingMod.me.is_replaying) { + if (RepeatingMod.input_replay != null && + RepeatingMod.input_replay.sprinting != null && + RepeatingMod.input_replay.sprinting != sprinting) { + ci.cancel(); + return; + } + } + } + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index a88da8c..594cbd0 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -12,6 +12,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.TickTask; +import java.util.ArrayList; + @Mixin(ClientPlayerEntity.class) public abstract class MovementMixin { @@ -30,36 +32,13 @@ public abstract class MovementMixin { }); } - @Inject(at = @At(value = "HEAD"), method = "tick") - private void onTickHead(CallbackInfo ci) { - for (TickTask t:TickTask.ticks) - if (t.getAt() == TickTask.TickAt.HEAD) - t.tick(); + @Inject(at = @At(value = "HEAD"), method = "tickMovement") + private void onMoveHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); } @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(CallbackInfo ci) { - for (TickTask t:TickTask.ticks) - if (t.getAt() == TickTask.TickAt.TAIL) - t.tick(); - } - - @Inject(at = @At(value = "HEAD"), method = "tickMovement") - private void onMove(CallbackInfo ci) { - if (RepeatingMod.me.is_recording) { - RepeatingMod.me.recordAllInput(); - } - } - - @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) - private void onSprint(boolean sprinting,CallbackInfo ci) { - if (RepeatingMod.me.is_replaying) { - if (RepeatingMod.input_replay != null && - RepeatingMod.input_replay.sprinting != null && - RepeatingMod.input_replay.sprinting != sprinting) { - ci.cancel(); - return; - } - } + private void onMoveTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); } } diff --git a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java index f4530fb..d4511b1 100644 --- a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java @@ -7,15 +7,17 @@ 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; +import themixray.repeating.mod.TickTask; @Mixin(GameRenderer.class) public abstract class RendererMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); + } + @Inject(at = @At(value = "TAIL"), method = "tick") private void onTickTail(CallbackInfo ci) { - if (RepeatingMod.me.is_replaying) { - if (RepeatingMod.input_replay != null) { - RepeatingMod.me.recordCameraInput(); - } - } + TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); } } diff --git a/src/main/resources/assets/repeating-mod/lang/en_us.json b/src/main/resources/assets/repeating-mod/lang/en_us.json index d9417ba..ee191bd 100644 --- a/src/main/resources/assets/repeating-mod/lang/en_us.json +++ b/src/main/resources/assets/repeating-mod/lang/en_us.json @@ -14,9 +14,9 @@ "text.repeating-mod.parkour": "Parkour mode", "text.repeating-mod.settings": "Settings", "text.repeating-mod.dev": "In development...", - "text.repeating-mod.nan_pos_delay": "No pos delay", - "text.repeating-mod.pos_delay": "Pos delay: %s ms", - "text.repeating-mod.pos_delay_text": "Delay after which the pos event is added", + "text.repeating-mod.nan_pos_delay": "No pos timer", + "text.repeating-mod.pos_delay": "Pos timer: %s ticks", + "text.repeating-mod.pos_delay_text": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", "message.repeating-mod.replay_start": "Replay started", "message.repeating-mod.replay_stop": "Replay finished", diff --git a/src/main/resources/assets/repeating-mod/lang/ru_ru.json b/src/main/resources/assets/repeating-mod/lang/ru_ru.json index ed68292..46ba2e1 100644 --- a/src/main/resources/assets/repeating-mod/lang/ru_ru.json +++ b/src/main/resources/assets/repeating-mod/lang/ru_ru.json @@ -14,9 +14,9 @@ "text.repeating-mod.parkour": "Режим паркура", "text.repeating-mod.settings": "Настройки", "text.repeating-mod.dev": "В разработке...", - "text.repeating-mod.nan_pos_delay": "Задержки позиции нету", - "text.repeating-mod.pos_delay": "Задержка позиции: %s мс", - "text.repeating-mod.pos_delay_text": "Задержка, после которой добавляется ивент позиции", + "text.repeating-mod.nan_pos_delay": "Таймера позиции нету", + "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", + "text.repeating-mod.pos_delay_text": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", "message.repeating-mod.replay_start": "Повтор начат", "message.repeating-mod.replay_stop": "Повтор закончен", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 30b9df6..e137afe 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "depends": { "fabricloader": ">=0.14.14", "fabric-api": "*", - "minecraft": "~1.19.3", + "minecraft": "1.19.x", "java": ">=17" }, "suggests": { diff --git a/src/main/resources/repeating-mod.mixins.json b/src/main/resources/repeating-mod.mixins.json index 50f497d..c03d7f6 100644 --- a/src/main/resources/repeating-mod.mixins.json +++ b/src/main/resources/repeating-mod.mixins.json @@ -7,7 +7,10 @@ ], "client": [ "MovementMixin", - "InputMixin" + "InputMixin", + "RendererMixin", + "EntityMixin", + "ClientMixin" ], "injectors": { "defaultRequire": 1 From 1ad16e6343a4463a5bbdee2f5bb51c39c772fc3a Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Wed, 31 May 2023 22:43:33 +0300 Subject: [PATCH 05/72] version 1.0.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8a30063..1d9b018 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.19.3+build.1 loader_version=0.14.17 # Mod Properties -mod_version = 1.0.4 +mod_version = 1.0.5 maven_group = themixray.repeating.mod archives_base_name = repeating-mod From e593859723f251414dabe0f1476548516c0672ea Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:46:07 +0300 Subject: [PATCH 06/72] 1.0.6 update removed owo lib fixed some bugs --- build.gradle | 15 +- gradle.properties | 12 +- .../themixray/repeating/mod/EasyConfig.java | 6 +- .../themixray/repeating/mod/RepeatingMod.java | 81 ++- .../repeating/mod/RepeatingScreen.java | 493 ++++++++++++------ .../repeating/mod/mixin/EntityMixin.java | 5 - .../repeating/mod/mixin/RendererMixin.java | 2 +- .../repeating/mod/render/RenderHelper.java | 200 +++++++ .../repeating/mod/render/RenderSystem.java | 13 + .../mod/render/buffer/BufferManager.java | 48 ++ .../repeating/mod/render/buffer/Vertex.java | 30 ++ .../mod/render/buffer/WorldBuffer.java | 82 +++ .../repeating/mod/render/shader/Shader.java | 42 ++ .../mod/render/shader/ShaderManager.java | 97 ++++ .../assets/renderer/shader/position_color.fsh | 12 + .../assets/renderer/shader/position_color.vsh | 13 + .../assets/repeating-mod/lang/en_us.json | 18 +- .../assets/repeating-mod/lang/ru_ru.json | 18 +- src/main/resources/fabric.mod.json | 2 +- 19 files changed, 990 insertions(+), 199 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/render/RenderHelper.java create mode 100644 src/main/java/themixray/repeating/mod/render/RenderSystem.java create mode 100644 src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java create mode 100644 src/main/java/themixray/repeating/mod/render/buffer/Vertex.java create mode 100644 src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java create mode 100644 src/main/java/themixray/repeating/mod/render/shader/Shader.java create mode 100644 src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java create mode 100644 src/main/resources/assets/renderer/shader/position_color.fsh create mode 100644 src/main/resources/assets/renderer/shader/position_color.vsh diff --git a/build.gradle b/build.gradle index fb32d3f..220e433 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,13 @@ repositories { } dependencies { + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + + //add joml + modImplementation 'org.joml:joml:1.10.4' + include 'org.joml:joml:1.10.4' + // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" @@ -30,13 +37,13 @@ dependencies { // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" - modImplementation "io.wispforest:owo-lib:${project.owo_version}" - // only if you plan to use owo-config - annotationProcessor "io.wispforest:owo-lib:${project.owo_version}" +// modImplementation "io.wispforest:owo-lib:${project.owo_version}" +// // only if you plan to use owo-config +// annotationProcessor "io.wispforest:owo-lib:${project.owo_version}" // include this if you don't want force your users to install owo // sentinel will warn them and give the option to download it automatically - include "io.wispforest:owo-sentinel:${project.owo_version}" +// include "io.wispforest:owo-sentinel:${project.owo_version}" } base { diff --git a/gradle.properties b/gradle.properties index 1d9b018..47a1114 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,16 +4,16 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.19.3 -yarn_mappings=1.19.3+build.1 -loader_version=0.14.17 +minecraft_version=1.20 +yarn_mappings=1.20+build.1 +loader_version=0.14.23 # Mod Properties -mod_version = 1.0.5 +mod_version = 1.0.6 maven_group = themixray.repeating.mod archives_base_name = repeating-mod # Dependencies -fabric_version=0.76.1+1.19.3 +fabric_version=0.83.0+1.20 -owo_version=0.10.3+1.19.3 +#owo_version=0.11.1+1.20 diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/themixray/repeating/mod/EasyConfig.java index eab45d1..8836722 100644 --- a/src/main/java/themixray/repeating/mod/EasyConfig.java +++ b/src/main/java/themixray/repeating/mod/EasyConfig.java @@ -71,10 +71,10 @@ public class EasyConfig { } private String toText(Map p) { - String t = ""; + StringBuilder t = new StringBuilder(); for (Map.Entry e:p.entrySet()) - t += e.getKey() + "=" + e.getValue() + "\n"; - return t; + t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); + return t.toString(); } private Map toMap(String j) { Map m = new HashMap<>(); diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 4ed30dd..068aa81 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -4,22 +4,39 @@ import com.google.common.collect.Lists; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.rendering.v1.DimensionRenderingRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.render.*; import net.minecraft.client.util.InputUtil; import net.minecraft.entity.MovementType; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.Formatting; import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL11; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import themixray.repeating.mod.render.RenderHelper; +import themixray.repeating.mod.render.RenderSystem; +import themixray.repeating.mod.render.buffer.WorldBuffer; +import java.awt.*; import java.util.*; +import java.util.List; public class RepeatingMod implements ClientModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); @@ -27,6 +44,9 @@ public class RepeatingMod implements ClientModInitializer { public static final FabricLoader loader = FabricLoader.getInstance(); public static RepeatingMod me; + public Vec3d start_record_pos = null; + public Vec3d finish_record_pos = null; + public List record = new ArrayList<>(); public boolean is_recording = false; public long last_record = -1; @@ -46,6 +66,8 @@ public class RepeatingMod implements ClientModInitializer { public long record_pos_delay = 20; + public static Random rand = new Random(); + public EasyConfig conf; @Override @@ -53,6 +75,50 @@ public class RepeatingMod implements ClientModInitializer { LOGGER.info("Repeating mod initialized"); me = this; + RenderSystem.init(); + WorldRenderEvents.LAST.register(context -> { + WorldBuffer buffer = RenderHelper.startTri(context); + if (start_record_pos != null) { + RenderHelper.drawRectFromTri(buffer, + (float) start_record_pos.getX() - 0.25F, + (float) start_record_pos.getY() + 0.01F, + (float) start_record_pos.getZ() - 0.25F, + + (float) start_record_pos.getX() + 0.25F, + (float) start_record_pos.getY() + 0.01F, + (float) start_record_pos.getZ() - 0.25F, + + (float) start_record_pos.getX() + 0.25F, + (float) start_record_pos.getY() + 0.01F, + (float) start_record_pos.getZ() + 0.25F, + + (float) start_record_pos.getX() - 0.25F, + (float) start_record_pos.getY() + 0.01F, + (float) start_record_pos.getZ() + 0.25F, + new Color(70,230,70,128)); + } + if (finish_record_pos != null) { + RenderHelper.drawRectFromTri(buffer, + (float) finish_record_pos.getX() - 0.25F, + (float) finish_record_pos.getY() + 0.01F, + (float) finish_record_pos.getZ() - 0.25F, + + (float) finish_record_pos.getX() + 0.25F, + (float) finish_record_pos.getY() + 0.01F, + (float) finish_record_pos.getZ() - 0.25F, + + (float) finish_record_pos.getX() + 0.25F, + (float) finish_record_pos.getY() + 0.01F, + (float) finish_record_pos.getZ() + 0.25F, + + (float) finish_record_pos.getX() - 0.25F, + (float) finish_record_pos.getY() + 0.01F, + (float) finish_record_pos.getZ() + 0.25F, + new Color(230,70,70,128)); + } + RenderHelper.endTri(buffer); + }); + Map def = new HashMap<>(); def.put("record_pos_delay", String.valueOf(record_pos_delay)); @@ -109,14 +175,15 @@ public class RepeatingMod implements ClientModInitializer { return null; } - public void startRecording() { is_recording = true; menu.update_btns(); record.clear(); - record.add(new RecordMoveEvent(client.player.getPos(), - client.player.getHeadYaw(), client.player.getPitch())); + finish_record_pos = null; + Vec3d v = client.player.getPos(); + record.add(new RecordMoveEvent(v,client.player.getHeadYaw(),client.player.getPitch())); + start_record_pos = v; if (record_pos_delay > 0) { move_tick = new TickTask( @@ -226,6 +293,7 @@ public class RepeatingMod implements ClientModInitializer { public void stopRecording() { is_recording = false; + finish_record_pos = client.player.getPos(); if (move_tick != null) { move_tick.cancel(); move_tick = null; @@ -285,10 +353,11 @@ public class RepeatingMod implements ClientModInitializer { return (double) Math.round(value * factor) / factor; } - public static void sendMessage(Text text) { + public static void sendMessage(MutableText text) { client.player.sendMessage(Text.literal("[") - .append(Text.translatable("text.repeating-mod.name")) - .append("] ").append(text)); + .append(Text.translatable("text.repeating-mod.name")) + .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) + .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); } public static void sendDebug(String s) { diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 5fe187e..7e2412c 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -1,192 +1,371 @@ 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 net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.OptionSliderWidget; +import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; -import org.jetbrains.annotations.NotNull; +import net.minecraft.util.math.Vec3d; -import java.io.*; +import java.awt.*; +import java.io.File; import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; -public class RepeatingScreen extends BaseOwoScreen { - public RepeatingMod mod; - public ButtonComponent replay_btn; - public ButtonComponent record_btn; - public ButtonComponent loop_btn; - public boolean was_build = false; - - public RepeatingScreen() { +@Environment(EnvType.CLIENT) +public class RepeatingScreen extends Screen { + protected RepeatingScreen() { + super(Text.literal("")); this.mod = RepeatingMod.me; } - @Override - protected @NotNull OwoUIAdapter createAdapter() { - return OwoUIAdapter.create(this, Containers::horizontalFlow); - } + public RepeatingMod mod; + + public ButtonWidget record_btn; + public ButtonWidget replay_btn; + public ButtonWidget loop_btn; + + public ButtonWidget export_btn; + public ButtonWidget import_btn; + + public SliderWidget pos_delay_slider; + + public boolean was_build = false; public void update_btns() { if (was_build) { replay_btn.setMessage(Text.translatable("text.repeating-mod." + - ((mod.is_replaying) ? "stop" : "start")).append(" ") - .append(Text.translatable("text.repeating-mod.replay"))); + ((mod.is_replaying) ? "stop_replay" : "start_replay"))); record_btn.setMessage(Text.translatable("text.repeating-mod." + - ((mod.is_recording) ? "stop" : "start")).append(" ") - .append(Text.translatable("text.repeating-mod.record"))); + ((mod.is_recording) ? "stop_record" : "start_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); + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context); +// context.drawCenteredTextWithShadow(textRenderer, +// Text.literal("You must see me"), +// width / 2, height / 2, +// Color.white.getRGB()); + super.render(context, mouseX, mouseY, delta); + } - 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) -> { + @Override + protected void init() { + record_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.start_record"), button -> { 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)); + }) + .dimensions(width / 2 - 60, height / 2 - 54, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) + .build(); + + replay_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.start_replay"), button -> { + if (!mod.is_recording) { + if (mod.is_replaying) + mod.stopReplay(); + else mod.startReplay(); + update_btns(); + } + }) + .dimensions(width / 2 - 60, height / 2 - 32, 98, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))) + .build(); + + loop_btn = ButtonWidget.builder(Text.of(""), button -> { + mod.loop_replay = !mod.loop_replay; + update_btns(); + }) + .dimensions(width / 2 + 40, height / 2 - 32, 20, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) + .build(); + + export_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.export"), button -> { + if (mod.finish_record_pos == null) return; + StringBuilder t = new StringBuilder(); + for (int i = 0; i < mod.record.size(); i++) { + t.append(mod.record.get(i).toText()); + t.append("\n"); + } + t.append(mod.start_record_pos.getX()+"n"+ + mod.start_record_pos.getY()+"n"+ + mod.start_record_pos.getZ()+"x"+ + mod.finish_record_pos.getX()+"n"+ + mod.finish_record_pos.getY()+"n"+ + mod.finish_record_pos.getZ()); + + File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); + if (!p.exists()) p.mkdir(); + File file = new File(p,"export_"+ + new SimpleDateFormat("MM_dd_yyyy").format(new Date()) + +"_"+RepeatingMod.rand.nextInt(10)+".txt"); + try { + if (!file.exists()) file.createNewFile(); + Files.write(file.toPath(), t.toString().getBytes()); + Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\""); + } catch (Exception e) { + e.printStackTrace(); + } + }) + .dimensions(width / 2 - 60, height / 2 - 10, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.export_tooltip"))) + .build(); + + import_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.import"), button -> { + 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()); + List ss = List.of(t.split("\n")); + String ls = ss.get(ss.size()-1); + ss = ss.subList(0,ss.size()-1); + for (String s:ss) + mod.record.add(RepeatingMod.RecordEvent.fromText(s)); + String[] lss0 = ls.split("x"); + String[] lss1 = lss0[0].split("n"); + String[] lss2 = lss0[1].split("n"); + mod.start_record_pos = new Vec3d( + Float.parseFloat(lss1[0]), + Float.parseFloat(lss1[1]), + Float.parseFloat(lss1[2])); + mod.finish_record_pos = new Vec3d( + Float.parseFloat(lss2[0]), + Float.parseFloat(lss2[1]), + Float.parseFloat(lss2[2])); + } catch (Exception e) { + e.printStackTrace(); + } + }) + .dimensions(width / 2 - 60, height / 2 + 12, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) + .build(); + + pos_delay_slider = new SliderWidget( + width / 2 - 60, height / 2 + 34, 120, 20, + (mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(mod.record_pos_delay)), + (mod.record_pos_delay/10d+1d)/101d) { + + @Override + protected void updateMessage() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10)))); + } + + @Override + protected void applyValue() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10)))); + mod.record_pos_delay = (long) (v*10); + mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); + mod.conf.save(); + } + + @Override + public void onRelease(double mouseX, double mouseY) { + super.onRelease(mouseX, mouseY); + applyValue(); + } + + @Override + protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { + super.onDrag(mouseX, mouseY, deltaX, deltaY); + applyValue(); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + updateMessage(); + } + }; + pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); + was_build = true; - rootComponent.child( - Containers.horizontalFlow(Sizing.content(), Sizing.content()).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 += "\n"; - } + update_btns(); - File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); - if (!p.exists()) p.mkdir(); - File file = new File(p,"export.txt"); + addDrawableChild(replay_btn); + addDrawableChild(loop_btn); + addDrawableChild(record_btn); + addDrawableChild(export_btn); + addDrawableChild(import_btn); + addDrawableChild(pos_delay_slider); - 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("\n")) - 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),-20,100) - .setFromDiscreteValue(mod.record_pos_delay) - .message((String s)->{ - mod.record_pos_delay = Long.parseLong(s); - mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); - mod.conf.save(); - if (mod.record_pos_delay > -1) - return Text.translatable("text.repeating-mod.pos_delay", s); - return Text.translatable("text.repeating-mod.nan_pos_delay"); - }).scrollStep(25) - .margins(Insets.of(1)) - .tooltip(Text.translatable("text.repeating-mod.pos_delay_text"))) - .padding(Insets.of(10)) - .surface(Surface.DARK_PANEL) - .verticalAlignment(VerticalAlignment.CENTER) - .horizontalAlignment(HorizontalAlignment.CENTER) - .margins(Insets.of(1))) - )); +// 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)); +// was_build = true; +// +// rootComponent.child( +// Containers.horizontalFlow(Sizing.content(), Sizing.content()).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 += "\n"; +// } +// +// 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("\n")) +// 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),-20,100) +// .setFromDiscreteValue(mod.record_pos_delay) +// .message((String s)->{ +// mod.record_pos_delay = Long.parseLong(s); +// mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); +// mod.conf.save(); +// if (mod.record_pos_delay > -1) +// return Text.translatable("text.repeating-mod.pos_delay", s); +// return Text.translatable("text.repeating-mod.nan_pos_delay"); +// }).scrollStep(25) +// .margins(Insets.of(1)) +// .tooltip(Text.translatable("text.repeating-mod.pos_delay_text"))) +// .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/EntityMixin.java b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java index 8b09ad0..94cfeb7 100644 --- a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java @@ -1,11 +1,6 @@ package themixray.repeating.mod.mixin; -import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.Entity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.hit.HitResult; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java index d4511b1..fcaca78 100644 --- a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java @@ -1,7 +1,7 @@ package themixray.repeating.mod.mixin; import net.minecraft.client.input.KeyboardInput; -import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.*; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/java/themixray/repeating/mod/render/RenderHelper.java b/src/main/java/themixray/repeating/mod/render/RenderHelper.java new file mode 100644 index 0000000..edbe614 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/RenderHelper.java @@ -0,0 +1,200 @@ +package themixray.repeating.mod.render; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.render.buffer.WorldBuffer; +import themixray.repeating.mod.render.shader.ShaderManager; + +import java.awt.*; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class RenderHelper { + public WorldBuffer startLines(WorldRenderContext context) { + glEnable(GL_LINE_SMOOTH); + return new WorldBuffer(GL_LINES, ShaderManager.getPositionColorShader(), context); + } + + public void endLines(WorldBuffer buffer) { + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + buffer.draw(); + glDepthMask(true); + glDisable(GL_BLEND); + } + + public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) { + buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + } + + public WorldBuffer startTri(WorldRenderContext context) { + return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context); + } + + public void endTri(WorldBuffer buffer) { + //glDepthRange(0, 0.7); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDepthMask(false); + buffer.draw(); + glDepthMask(true); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthRange(0, 1); + } + + public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) { + buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + } + + public void drawRectFromTri(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + Color color) { + drawTri(buffer, + x1, y1, z1, + x2, y2, z2, + x3, y3, z3, + color); + drawTri(buffer, + x3, y3, z3, + x4, y4, z4, + x1, y1, z1, + color); + } + + public void drawRectFromLines(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + Color color) { + drawLine(buffer, + x1, y1, z1, + x2, y2, z2, + color); + drawLine(buffer, + x2, y2, z2, + x3, y3, z3, + color); + drawLine(buffer, + x3, y3, z3, + x4, y4, z4, + color); + drawLine(buffer, + x4, y4, z4, + x1, y1, z1, + color); + } + + public void drawBoxFromTri(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + Color color) { + float[][] v = new float[][]{ + new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, + new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[0][0], v[0][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[0][0], v[0][1], v[1][2], + v[0][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[1][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[1][0], v[1][1], v[0][2], + color); + } + + public void drawBoxFromLines(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + Color color) { + float[][] v = new float[][]{ + new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, + new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[0][0], v[0][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[0][0], v[0][1], v[1][2], + v[0][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[1][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[1][0], v[1][1], v[0][2], + color); + } +} diff --git a/src/main/java/themixray/repeating/mod/render/RenderSystem.java b/src/main/java/themixray/repeating/mod/render/RenderSystem.java new file mode 100644 index 0000000..2a095f7 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/RenderSystem.java @@ -0,0 +1,13 @@ +package themixray.repeating.mod.render; + +import lombok.experimental.UtilityClass; +import themixray.repeating.mod.render.buffer.BufferManager; +import themixray.repeating.mod.render.shader.ShaderManager; + +@UtilityClass +public class RenderSystem { + public void init() { + BufferManager.init(); + ShaderManager.init(); + } +} diff --git a/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java b/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java new file mode 100644 index 0000000..4c3ae98 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java @@ -0,0 +1,48 @@ +package themixray.repeating.mod.render.buffer; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; + +import java.nio.FloatBuffer; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class BufferManager { + private int vao; + private int vbo; + + private int prevVao; + + public void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(client -> { + vao = glGenVertexArrays(); + vbo = glGenBuffers(); + }); + } + + public void bindBuffer() { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + } + + public void unbindBuffer() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + public void writeBuffer(FloatBuffer buffer) { + glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); + } + + public void draw(int drawMode, int verts) { + glDrawArrays(drawMode, 0, verts); + } + + public void bind() { + prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING); + glBindVertexArray(vao); + } + + public void unbind() { + glBindVertexArray(prevVao); + } +} diff --git a/src/main/java/themixray/repeating/mod/render/buffer/Vertex.java b/src/main/java/themixray/repeating/mod/render/buffer/Vertex.java new file mode 100644 index 0000000..5aaa113 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/buffer/Vertex.java @@ -0,0 +1,30 @@ +package themixray.repeating.mod.render.buffer; + +import lombok.Getter; + +public class Vertex { + @Getter + private float x; + @Getter + private float y; + @Getter + private float z; + @Getter + private float r; + @Getter + private float g; + @Getter + private float b; + @Getter + private float a; + + public Vertex(float x, float y, float z, float r, float g, float b, float a) { + this.x = x; + this.y = y; + this.z = z; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java b/src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java new file mode 100644 index 0000000..390f97d --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java @@ -0,0 +1,82 @@ +package themixray.repeating.mod.render.buffer; + +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.util.math.Vec3d; +import org.apache.commons.lang3.ArrayUtils; +import org.joml.Matrix4f; +import org.lwjgl.BufferUtils; +import themixray.repeating.mod.render.shader.Shader; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.opengl.GL33.*; + +public class WorldBuffer { + private final List vertices = new ArrayList<>(); + private final int drawMode; + private final Shader shader; + private FloatBuffer projectionMatrix; + private final Vec3d cameraPos; + + public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) { + this.drawMode = drawMode; + this.shader = shader; + this.cameraPos = worldRenderContext.camera().getPos(); + makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix()); + } + + public void vert(float x, float y, float z, float r, float g, float b, float a) { + vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a)); + } + + public void draw() { + BufferManager.bind(); + BufferManager.bindBuffer(); + + BufferManager.writeBuffer(getBuffer()); + applyProjectionMatrix(); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, vertices.size() * 3 * 4L); + glEnableVertexAttribArray(1); + + BufferManager.unbindBuffer(); + + shader.bind(); + BufferManager.draw(drawMode, this.vertices.size()); + shader.unbind(); + + BufferManager.unbind(); + } + + private FloatBuffer getBuffer() { + FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7); + ArrayList floats = new ArrayList<>(); + for (Vertex vertex : vertices) { + floats.add(vertex.getX()); + floats.add(vertex.getY()); + floats.add(vertex.getZ()); + } + for (Vertex vertex : vertices) { + floats.add(vertex.getR()); + floats.add(vertex.getG()); + floats.add(vertex.getB()); + floats.add(vertex.getA()); + } + Float[] floatArray = new Float[floats.size()]; + floats.toArray(floatArray); + floatBuffer.put(ArrayUtils.toPrimitive(floatArray)); + return floatBuffer.flip(); + } + + private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) { + this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16)); + } + + private void applyProjectionMatrix() { + shader.uniformMatrix4f("u_projection", projectionMatrix); + } +} diff --git a/src/main/java/themixray/repeating/mod/render/shader/Shader.java b/src/main/java/themixray/repeating/mod/render/shader/Shader.java new file mode 100644 index 0000000..e11dd9a --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/shader/Shader.java @@ -0,0 +1,42 @@ +package themixray.repeating.mod.render.shader; + +import lombok.Getter; + +import java.nio.FloatBuffer; + +import static org.lwjgl.opengl.GL33.*; + +public class Shader { + @Getter + private final int id; + + + public Shader(String name) { + int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX); + int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT); + this.id = glCreateProgram(); + glAttachShader(id, v); + glAttachShader(id, f); + glLinkProgram(id); + } + + public void bind() { + glUseProgram(id); + } + + public void unbind() { + glUseProgram(0); + } + + public void uniformMatrix4f(String name, FloatBuffer matrix) { + bind(); + glUniformMatrix4fv(glGetUniformLocation(id, name), false, matrix); + unbind(); + } + + public void uniformValue2f(String name, float value1, float value2) { + bind(); + glUniform2f(glGetUniformLocation(id, name), value1, value2); + unbind(); + } +} diff --git a/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java b/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java new file mode 100644 index 0000000..f11c98b --- /dev/null +++ b/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java @@ -0,0 +1,97 @@ +package themixray.repeating.mod.render.shader; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.TextureUtil; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.GlImportProcessor; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceFactory; +import net.minecraft.util.Identifier; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class ShaderManager { + @Getter + private Shader positionColorShader; + + public void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders()); + } + + private void loadShaders() { + positionColorShader = new Shader("position_color"); + } + + public int loadShaderProgram(String name, ShaderType type) { + try { + boolean file_present = true; + ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager(); + Optional resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension)); + int i = glCreateShader(type.glType); + if (resource.isPresent()) { + GlStateManager.glShaderSource(i, new GlImportProcessor() { + @SneakyThrows + @Nullable + @Override + public String loadImport(boolean inline, String name) { + return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8); + } + }.readSource(readResourceAsString(resource.get().getInputStream()))); + } else file_present = false; + glCompileShader(i); + if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) { + String shaderInfo = StringUtils.trim(glGetShaderInfoLog(i, 32768)); + throw new IOException("Couldn't compile " + type.name + " program (" + name + ") : " + shaderInfo); + } + return i; + } catch (IOException e) { + e.printStackTrace(); + } + return 0; + } + + private String readResourceAsString(InputStream inputStream) { + ByteBuffer byteBuffer = null; + try { + byteBuffer = TextureUtil.readResource(inputStream); + int i = byteBuffer.position(); + byteBuffer.rewind(); + return MemoryUtil.memASCII(byteBuffer, i); + } catch (IOException ignored) { + } finally { + if (byteBuffer != null) { + MemoryUtil.memFree(byteBuffer); + } + } + return null; + } + + public enum ShaderType { + VERTEX("vertex", ".vsh", GL_VERTEX_SHADER), + FRAGMENT("fragment", ".fsh", GL_FRAGMENT_SHADER); + private final String name; + private final String fileExtension; + private final int glType; + + ShaderType(String name, String fileExtension, int glType) { + this.name = name; + this.fileExtension = fileExtension; + this.glType = glType; + } + } +} diff --git a/src/main/resources/assets/renderer/shader/position_color.fsh b/src/main/resources/assets/renderer/shader/position_color.fsh new file mode 100644 index 0000000..aecb5f7 --- /dev/null +++ b/src/main/resources/assets/renderer/shader/position_color.fsh @@ -0,0 +1,12 @@ +#version 330 + +in vec4 vertexColor; + +out vec4 fragmentColor; + +void main() { + if (vertexColor.a == 0.0f) { + discard; + } + fragmentColor = vertexColor; +} \ No newline at end of file diff --git a/src/main/resources/assets/renderer/shader/position_color.vsh b/src/main/resources/assets/renderer/shader/position_color.vsh new file mode 100644 index 0000000..1f31658 --- /dev/null +++ b/src/main/resources/assets/renderer/shader/position_color.vsh @@ -0,0 +1,13 @@ +#version 330 + +layout (location = 0) in vec3 i_pos; +layout (location = 1) in vec4 i_color; + +uniform mat4 u_projection; + +out vec4 vertexColor; + +void main() { + gl_Position = u_projection * vec4(i_pos, 1.0f); + vertexColor = i_color; +} \ No newline at end of file diff --git a/src/main/resources/assets/repeating-mod/lang/en_us.json b/src/main/resources/assets/repeating-mod/lang/en_us.json index ee191bd..cfab5c4 100644 --- a/src/main/resources/assets/repeating-mod/lang/en_us.json +++ b/src/main/resources/assets/repeating-mod/lang/en_us.json @@ -4,19 +4,21 @@ "key.repeating-mod.toggle_record": "Toggle recording", "text.repeating-mod.name": "Repeating Mod", - "text.repeating-mod.record": "record", - "text.repeating-mod.replay": "replay", - "text.repeating-mod.start": "Start", - "text.repeating-mod.stop": "Stop", + "text.repeating-mod.start_record": "Start record", + "text.repeating-mod.stop_record": "Stop record", + "text.repeating-mod.start_replay": "Start replay", + "text.repeating-mod.stop_replay": "Stop replay", + "text.repeating-mod.record_tooltip": "Start/stop recording all activities", + "text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions", + "text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay", "text.repeating-mod.export": "Export record", "text.repeating-mod.import": "Import record", - "text.repeating-mod.basic": "Basic mode", - "text.repeating-mod.parkour": "Parkour mode", - "text.repeating-mod.settings": "Settings", + "text.repeating-mod.export_tooltip": "Exporting a recording to a file", + "text.repeating-mod.import_tooltip": "Importing an entry from the import.txt file", "text.repeating-mod.dev": "In development...", "text.repeating-mod.nan_pos_delay": "No pos timer", "text.repeating-mod.pos_delay": "Pos timer: %s ticks", - "text.repeating-mod.pos_delay_text": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", + "text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", "message.repeating-mod.replay_start": "Replay started", "message.repeating-mod.replay_stop": "Replay finished", diff --git a/src/main/resources/assets/repeating-mod/lang/ru_ru.json b/src/main/resources/assets/repeating-mod/lang/ru_ru.json index 46ba2e1..3fd30f4 100644 --- a/src/main/resources/assets/repeating-mod/lang/ru_ru.json +++ b/src/main/resources/assets/repeating-mod/lang/ru_ru.json @@ -4,19 +4,21 @@ "key.repeating-mod.toggle_record": "Вкл/выкл запись", "text.repeating-mod.name": "Репитинг Мод", - "text.repeating-mod.record": "запись", - "text.repeating-mod.replay": "повтор", - "text.repeating-mod.start": "Начать", - "text.repeating-mod.stop": "Остановить", + "text.repeating-mod.start_record": "Начать запись", + "text.repeating-mod.stop_record": "Остановить запись", + "text.repeating-mod.start_replay": "Начать повтор", + "text.repeating-mod.stop_replay": "Остановить повтор", + "text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий", + "text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий", + "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор повтора записанных действий", "text.repeating-mod.export": "Экспорт записи", "text.repeating-mod.import": "Импорт записи", - "text.repeating-mod.basic": "Обычный режим", - "text.repeating-mod.parkour": "Режим паркура", - "text.repeating-mod.settings": "Настройки", + "text.repeating-mod.export_tooltip": "Экспорт записи в файл", + "text.repeating-mod.import_tooltip": "Импорт записи из файла import.txt", "text.repeating-mod.dev": "В разработке...", "text.repeating-mod.nan_pos_delay": "Таймера позиции нету", "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", - "text.repeating-mod.pos_delay_text": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", + "text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", "message.repeating-mod.replay_start": "Повтор начат", "message.repeating-mod.replay_stop": "Повтор закончен", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e137afe..cdf4d39 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "depends": { "fabricloader": ">=0.14.14", "fabric-api": "*", - "minecraft": "1.19.x", + "minecraft": ">=1.20", "java": ">=17" }, "suggests": { From 543f2a18cf78ac67700f6a3d881293af140e880c Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Fri, 20 Oct 2023 22:27:39 +0300 Subject: [PATCH 07/72] fixed 1.20.2 incompatibility --- gradle.properties | 2 +- src/main/java/themixray/repeating/mod/RepeatingScreen.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 47a1114..6be8dc4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.20+build.1 loader_version=0.14.23 # Mod Properties -mod_version = 1.0.6 +mod_version = 1.0.7 maven_group = themixray.repeating.mod archives_base_name = repeating-mod diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 7e2412c..dba6192 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -52,7 +52,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context); + renderBackgroundTexture(context); // context.drawCenteredTextWithShadow(textRenderer, // Text.literal("You must see me"), // width / 2, height / 2, From e95f520b142279ebb054493372f2d809a7569027 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:52:33 +0300 Subject: [PATCH 08/72] Delete LICENSE --- LICENSE | 121 -------------------------------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0e259d4..0000000 --- a/LICENSE +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. From f3e5cfabf6b8680b5c16b2e4e0b6d36f67a2244a Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:56:53 +0300 Subject: [PATCH 09/72] Create LICENSE --- LICENSE | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8b1a9d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. From f3acb4df108eeb53ae2582d8eaa204fb73f232b3 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 20 Apr 2024 18:39:35 +0300 Subject: [PATCH 10/72] small refactoring --- gradle.properties | 12 +- .../themixray/repeating/mod/RecordFile.java | 104 +++++ .../themixray/repeating/mod/RecordList.java | 5 + .../themixray/repeating/mod/RepeatingMod.java | 359 +----------------- .../repeating/mod/RepeatingScreen.java | 227 +++-------- .../mod/events/RecordBlockBreakEvent.java | 32 ++ .../mod/events/RecordBlockInteractEvent.java | 46 +++ .../mod/events/RecordDelayEvent.java | 29 ++ .../repeating/mod/events/RecordEvent.java | 28 ++ .../mod/events/RecordInputEvent.java | 147 +++++++ .../repeating/mod/events/RecordMoveEvent.java | 42 ++ .../repeating/mod/mixin/MovementMixin.java | 8 +- .../repeating/mod/mixin/NetworkMixin.java | 32 ++ .../repeating/mod/render/RenderHelper.java | 16 +- .../repeating/mod/render/RenderSystem.java | 2 +- 15 files changed, 531 insertions(+), 558 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/RecordFile.java create mode 100644 src/main/java/themixray/repeating/mod/RecordList.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordEvent.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordInputEvent.java create mode 100644 src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java create mode 100644 src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java diff --git a/gradle.properties b/gradle.properties index 6be8dc4..9109b7d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,12 +8,10 @@ minecraft_version=1.20 yarn_mappings=1.20+build.1 loader_version=0.14.23 -# Mod Properties -mod_version = 1.0.7 -maven_group = themixray.repeating.mod -archives_base_name = repeating-mod - -# Dependencies +#Fabric api fabric_version=0.83.0+1.20 -#owo_version=0.11.1+1.20 +# Mod Properties +mod_version = 1.0.6 +maven_group = themixray.repeating.mod +archives_base_name = repeating-mod \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/RecordFile.java b/src/main/java/themixray/repeating/mod/RecordFile.java new file mode 100644 index 0000000..5508e16 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/RecordFile.java @@ -0,0 +1,104 @@ +package themixray.repeating.mod; + +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.events.RecordEvent; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +public class RecordFile { + private final File file; + private String name; + private String date; + private String author; + + private List events; + private Vec3d start_record_pos; + private Vec3d finish_record_pos; + + public RecordFile(File file, + String name, + String date, + String author, + List events, + Vec3d start_record_pos, + Vec3d finish_record_pos) { + this.file = file; + this.name = name; + this.date = date; + this.author = author; + + this.events = events; + this.start_record_pos = start_record_pos; + this.finish_record_pos = finish_record_pos; + } + + public File getFile() { + return file; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + public String getDate() { + return date; + } + + public void setAuthor(String author) { + this.author = author; + } + + public void setDate(String date) { + this.date = date; + } + + public void setName(String name) { + this.name = name; + } + + public void writeToMod() { + + } + + public void writeToFile(File file) { + + } + + public static void readFromMod() { + + } + + public static RecordFile readFromFile(File file) throws IOException { + String text = Files.readString(file.toPath()); + + List lines = List.of(text.split("\n")); + String last_line = lines.get(lines.size()-1); + lines = lines.subList(0,lines.size()-1); + + List + + for (String line: lines) + RepeatingMod.me.record.add(RecordEvent.deserialize(line)); + + String[] lss0 = ls.split("x"); + String[] lss1 = lss0[0].split("n"); + String[] lss2 = lss0[1].split("n"); + RepeatingMod.me.start_record_pos = new Vec3d( + Float.parseFloat(lss1[0]), + Float.parseFloat(lss1[1]), + Float.parseFloat(lss1[2])); + RepeatingMod.me.finish_record_pos = new Vec3d( + Float.parseFloat(lss2[0]), + Float.parseFloat(lss2[1]), + Float.parseFloat(lss2[2])); + RepeatingMod.sendMessage(Text.literal("")); + } +} diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java new file mode 100644 index 0000000..907989f --- /dev/null +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -0,0 +1,5 @@ +package themixray.repeating.mod; + +public class RecordList { + +} diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/RepeatingMod.java index 068aa81..151c8af 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingMod.java +++ b/src/main/java/themixray/repeating/mod/RepeatingMod.java @@ -4,32 +4,22 @@ import com.google.common.collect.Lists; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.fabricmc.fabric.api.client.rendering.v1.DimensionRenderingRegistry; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; -import net.minecraft.client.render.*; import net.minecraft.client.util.InputUtil; -import net.minecraft.entity.MovementType; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; import net.minecraft.text.MutableText; -import net.minecraft.text.Style; import net.minecraft.text.Text; -import net.minecraft.text.TextColor; import net.minecraft.util.Formatting; -import net.minecraft.util.Hand; -import net.minecraft.util.Identifier; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import org.lwjgl.glfw.GLFW; -import org.lwjgl.opengl.GL11; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import themixray.repeating.mod.events.RecordDelayEvent; +import themixray.repeating.mod.events.RecordEvent; +import themixray.repeating.mod.events.RecordInputEvent; +import themixray.repeating.mod.events.RecordMoveEvent; import themixray.repeating.mod.render.RenderHelper; import themixray.repeating.mod.render.RenderSystem; import themixray.repeating.mod.render.buffer.WorldBuffer; @@ -256,41 +246,6 @@ public class RepeatingMod implements ClientModInitializer { } } - public void recordCameraInput() { - RecordInputEvent l = ((RecordInputEvent)getLastRecord("input")); - if (l == null) { - RecordInputEvent e = new RecordInputEvent( - client.player.input.sneaking, - client.player.input.jumping, - client.player.input.movementSideways, - client.player.input.movementForward, - client.player.input.pressingForward, - client.player.input.pressingBack, - client.player.input.pressingLeft, - client.player.input.pressingRight, - client.player.getHeadYaw(), - client.player.getBodyYaw(), - client.player.getPitch(), - client.player.isSprinting(), - client.player.getYaw(), - client.player.getMovementSpeed()); - recordTick(e); - } else { - RecordInputEvent e = new RecordInputEvent(null,null,null, - null,null,null,null,null, - client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(), - null,client.player.getYaw(),client.player.getMovementSpeed()); - - if (!(e.yaw == l.yaw && - e.head_yaw == l.head_yaw && - e.pitch == l.pitch && - e.body_yaw == l.body_yaw)) { - e.fillEmpty(l); - recordTick(e); - } - } - } - public void stopRecording() { is_recording = false; finish_record_pos = client.player.getPos(); @@ -318,7 +273,7 @@ public class RepeatingMod implements ClientModInitializer { if (e instanceof RecordDelayEvent) { setDelay(((RecordDelayEvent) e).delay); } else { - e.callback(); + e.replay(); } replay_index++; @@ -347,12 +302,6 @@ public class RepeatingMod implements ClientModInitializer { sendMessage(Text.translatable("message.repeating-mod.replay_stop")); } - public static double round(double value, int places) { - if (places < 0) throw new IllegalArgumentException(); - long factor = (long) Math.pow(10, places); - return (double) Math.round(value * factor) / factor; - } - public static void sendMessage(MutableText text) { client.player.sendMessage(Text.literal("[") .append(Text.translatable("text.repeating-mod.name")) @@ -363,302 +312,4 @@ public class RepeatingMod implements ClientModInitializer { public static void sendDebug(String s) { client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); } - - public static abstract class RecordEvent { - abstract void callback(); - abstract String toText(); - abstract String getType(); - - public static RecordEvent fromText(String t) { - try { - String type = String.valueOf(t.charAt(0)); - String[] args = t.substring(2).split("&"); - if (type.equals("d")) { - return RecordDelayEvent.fromArgs(args); - } else if (type.equals("m")) { - return RecordMoveEvent.fromArgs(args); - } else if (type.equals("p")) { - return RecordInputEvent.fromArgs(args); - } else if (type.equals("b")) { - return RecordBlockBreakEvent.fromArgs(args); - } else if (type.equals("i")) { - return RecordBlockInteractEvent.fromArgs(args); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - } - - public static class RecordDelayEvent extends RecordEvent { - public long delay; - - public static RecordDelayEvent fromArgs(String[] a) { - return new RecordDelayEvent(Long.parseLong(a[0])); - } - - public RecordDelayEvent(long delay) { - this.delay = delay; - } - - public void callback() { - try { - Thread.sleep(delay/20*1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public String toText() { - return "d="+delay; - } - public String getType() { - return "delay"; - } - } - - public static class RecordMoveEvent extends RecordEvent { - public Vec3d vec; - public float yaw; - public float pitch; - - public static RecordMoveEvent fromArgs(String[] a) { - return new RecordMoveEvent(new Vec3d( - Double.parseDouble(a[0]), - Double.parseDouble(a[1]), - Double.parseDouble(a[2])), - Float.parseFloat(a[3]), - Float.parseFloat(a[4])); - } - - public RecordMoveEvent(Vec3d vec,float yaw,float pitch) { - this.vec = vec; - this.yaw = yaw; - this.pitch = pitch; - } - - public void callback() { - Vec3d p = client.player.getPos(); - Vec3d v = new Vec3d(vec.getX()-p.getX(),vec.getY()-p.getY(),vec.getZ()-p.getZ()); - client.player.move(MovementType.SELF,v); - client.player.setYaw(yaw); - client.player.setPitch(pitch); - } - - public String toText() { - return "m="+vec.getX()+"&"+vec.getY()+"&"+vec.getZ()+"&"+yaw+"&"+pitch; - } - public String getType() { - return "move"; - } - } - - public static class RecordInputEvent extends RecordEvent { - public Boolean sneaking; - public Boolean jumping; - public Boolean pressingForward; - public Boolean pressingBack; - public Boolean pressingLeft; - public Boolean pressingRight; - public Boolean sprinting; - - public Float movementSideways; - public Float movementForward; - - public float yaw; - public float head_yaw; - public float body_yaw; - public float pitch; - public float speed; - - public static RecordInputEvent fromArgs(String[] a) { - return new RecordInputEvent( - (a[0].equals("n")?null:a[0].equals("1")), - (a[1].equals("n")?null:a[1].equals("1")), - (a[2].equals("n")?null:Float.parseFloat(a[2])), - (a[3].equals("n")?null:Float.parseFloat(a[3])), - (a[4].equals("n")?null:a[4].equals("1")), - (a[5].equals("n")?null:a[5].equals("1")), - (a[6].equals("n")?null:a[6].equals("1")), - (a[7].equals("n")?null:a[7].equals("1")), - Float.parseFloat(a[8]),Float.parseFloat(a[9]), - Float.parseFloat(a[10]), - (a[11].equals("n")?null:a[11].equals("1")), - Float.parseFloat(a[12]), - Float.parseFloat(a[13])); - } - - public RecordInputEvent(Boolean sneaking, - Boolean jumping, - Float movementSideways, - Float movementForward, - Boolean pressingForward, - Boolean pressingBack, - Boolean pressingLeft, - Boolean pressingRight, - float head_yaw, - float body_yaw, - float head_pitch, - Boolean sprinting, - float yaw, - float speed) { - this.sneaking = sneaking; - this.jumping = jumping; - this.movementSideways = movementSideways; - this.movementForward = movementForward; - this.pressingForward = pressingForward; - this.pressingBack = pressingBack; - this.pressingLeft = pressingLeft; - this.pressingRight = pressingRight; - this.head_yaw = head_yaw; - this.body_yaw = body_yaw; - this.pitch = head_pitch; - this.sprinting = sprinting; - this.yaw = yaw; - this.speed = speed; - } - - public void fillEmpty(RecordInputEvent e) { - if (sneaking == null) sneaking = e.sneaking; - if (jumping == null) jumping = e.jumping; - if (movementSideways == null) movementSideways = e.movementSideways; - if (movementForward == null) movementForward = e.movementForward; - if (pressingForward == null) pressingForward = e.pressingForward; - if (pressingBack == null) pressingBack = e.pressingBack; - if (pressingLeft == null) pressingLeft = e.pressingLeft; - if (pressingRight == null) pressingRight = e.pressingRight; - if (sprinting == null) sprinting = e.sprinting; - } - - public boolean isEmpty() { - return sneaking == null && - jumping == null && - movementSideways == null && - movementForward == null && - pressingForward == null && - pressingBack == null && - pressingLeft == null && - pressingRight == null && - sprinting == null; - } - - public void callback() { - input_replay = this; - } - - public void inputCallback() { - if (sprinting != null && client.player.isSprinting() != sprinting) - client.player.setSprinting(sprinting); - if (client.player.getYaw() != yaw) - client.player.setYaw(yaw); - if (client.player.getHeadYaw() != head_yaw) - client.player.setHeadYaw(head_yaw); - if (client.player.getBodyYaw() != body_yaw) - client.player.setBodyYaw(body_yaw); - if (client.player.getPitch() != pitch) - client.player.setPitch(pitch); - if (client.player.getMovementSpeed() != speed) - client.player.setMovementSpeed(speed); - if (sneaking != null && client.player.input.sneaking != sneaking) - client.player.input.sneaking = sneaking; - if (jumping != null && client.player.input.jumping != jumping) - client.player.input.jumping = jumping; - if (movementSideways != null && client.player.input.movementSideways != movementSideways) - client.player.input.movementSideways = movementSideways; - if (movementForward != null && client.player.input.movementForward != movementForward) - client.player.input.movementForward = movementForward; - if (pressingForward != null && client.player.input.pressingForward != pressingForward) - client.player.input.pressingForward = pressingForward; - if (pressingBack != null && client.player.input.pressingBack != pressingBack) - client.player.input.pressingBack = pressingBack; - if (pressingLeft != null && client.player.input.pressingLeft != pressingLeft) - client.player.input.pressingLeft = pressingLeft; - if (pressingRight != null && client.player.input.pressingRight != pressingRight) - client.player.input.pressingRight = pressingRight; - } - - public String toText() { - return "p="+ - ((sneaking==null)?"n":(sneaking?"1":"0"))+"&"+ - ((jumping==null)?"n":(jumping?"1":"0"))+"&"+ - ((movementSideways==null)?"n":movementSideways)+"&"+ - ((movementForward==null)?"n":movementForward)+"&"+ - ((pressingForward==null)?"n":(pressingForward?"1":"0"))+"&"+ - ((pressingBack==null)?"n":(pressingBack?"1":"0"))+"&"+ - ((pressingLeft==null)?"n":(pressingLeft?"1":"0"))+"&"+ - ((pressingRight==null)?"n":(pressingRight?"1":"0"))+"&"+ - head_yaw+"&"+body_yaw+"&"+ pitch +"&"+ - ((sprinting==null)?"n":(sprinting?"1":"0")+ - "&"+yaw+"&"+speed); - } - - public String getType() { - return "input"; - } - } - - public static class RecordBlockBreakEvent extends RecordEvent { - public BlockPos pos; - - public static RecordBlockBreakEvent fromArgs(String[] a) { - return new RecordBlockBreakEvent(new BlockPos( - Integer.parseInt(a[0]), - Integer.parseInt(a[1]), - Integer.parseInt(a[2]))); - } - - public RecordBlockBreakEvent( - BlockPos pos) { - this.pos = pos; - } - - public void callback() { - client.interactionManager.breakBlock(pos); - } - - public String toText() { - return "b="+pos.getX()+"&"+pos.getY()+"&"+pos.getZ(); - } - public String getType() { - return "block_break"; - } - } - - public static class RecordBlockInteractEvent extends RecordEvent { - public Hand hand; - public BlockHitResult hitResult; - - public static RecordBlockInteractEvent fromArgs(String[] a) { - return new RecordBlockInteractEvent( - Hand.valueOf(a[5]), - new BlockHitResult(new Vec3d( - Double.parseDouble(a[0]), - Double.parseDouble(a[1]), - Double.parseDouble(a[2])), - Direction.byId(Integer.parseInt(a[4])), - new BlockPos( - Integer.parseInt(a[0]), - Integer.parseInt(a[1]), - Integer.parseInt(a[2])), - a[3].equals("1"))); - } - - public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { - this.hand = hand; - this.hitResult = hitResult; - } - - public void callback() { - client.interactionManager.interactBlock(client.player,hand,hitResult); - } - - public String toText() { - return "i="+hitResult.getBlockPos().getX()+"&"+hitResult.getBlockPos().getY()+"&"+hitResult.getBlockPos().getZ()+ - "&"+(hitResult.isInsideBlock()?"1":"0")+"&"+hitResult.getSide().getId()+"&"+hand.name(); - } - public String getType() { - return "block_interact"; - } - } } diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index dba6192..6679c99 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -7,16 +7,16 @@ import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.OptionSliderWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.events.RecordEvent; +import javax.swing.*; import java.awt.*; import java.io.File; import java.nio.file.Files; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Date; import java.util.List; @@ -52,11 +52,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackgroundTexture(context); -// context.drawCenteredTextWithShadow(textRenderer, -// Text.literal("You must see me"), -// width / 2, height / 2, -// Color.white.getRGB()); + renderBackground(context); super.render(context, mouseX, mouseY, delta); } @@ -101,7 +97,7 @@ public class RepeatingScreen extends Screen { if (mod.finish_record_pos == null) return; StringBuilder t = new StringBuilder(); for (int i = 0; i < mod.record.size(); i++) { - t.append(mod.record.get(i).toText()); + t.append(mod.record.get(i).serialize()); t.append("\n"); } t.append(mod.start_record_pos.getX()+"n"+ @@ -134,33 +130,43 @@ public class RepeatingScreen extends Screen { 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; + JFileChooser fc = new JFileChooser() { + @Override + protected JDialog createDialog(Component parent) throws HeadlessException { + JDialog dialog = super.createDialog(parent); + dialog.setLocationByPlatform(true); + dialog.setAlwaysOnTop(true); + return dialog; + } + + }; + + int retValue = fc.showOpenDialog(null); + if (retValue == JFileChooser.APPROVE_OPTION){ + File file = fc.getSelectedFile(); + try { + String t = Files.readString(file.toPath()); + List ss = List.of(t.split("\n")); + String ls = ss.get(ss.size()-1); + ss = ss.subList(0,ss.size()-1); + for (String s:ss) + mod.record.add(RecordEvent.deserialize(s)); + String[] lss0 = ls.split("x"); + String[] lss1 = lss0[0].split("n"); + String[] lss2 = lss0[1].split("n"); + mod.start_record_pos = new Vec3d( + Float.parseFloat(lss1[0]), + Float.parseFloat(lss1[1]), + Float.parseFloat(lss1[2])); + mod.finish_record_pos = new Vec3d( + Float.parseFloat(lss2[0]), + Float.parseFloat(lss2[1]), + Float.parseFloat(lss2[2])); + RepeatingMod.sendMessage(Text.literal("")); + } catch (Exception e) { + e.printStackTrace(); } - String t = Files.readString(file.toPath()); - List ss = List.of(t.split("\n")); - String ls = ss.get(ss.size()-1); - ss = ss.subList(0,ss.size()-1); - for (String s:ss) - mod.record.add(RepeatingMod.RecordEvent.fromText(s)); - String[] lss0 = ls.split("x"); - String[] lss1 = lss0[0].split("n"); - String[] lss2 = lss0[1].split("n"); - mod.start_record_pos = new Vec3d( - Float.parseFloat(lss1[0]), - Float.parseFloat(lss1[1]), - Float.parseFloat(lss1[2])); - mod.finish_record_pos = new Vec3d( - Float.parseFloat(lss2[0]), - Float.parseFloat(lss2[1]), - Float.parseFloat(lss2[2])); - } catch (Exception e) { - e.printStackTrace(); } }) .dimensions(width / 2 - 60, height / 2 + 12, 120, 20) @@ -171,21 +177,21 @@ public class RepeatingScreen extends Screen { width / 2 - 60, height / 2 + 34, 120, 20, (mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : Text.translatable("text.repeating-mod.pos_delay", String.valueOf(mod.record_pos_delay)), - (mod.record_pos_delay/10d+1d)/101d) { + (mod.record_pos_delay+1d)/101d) { @Override protected void updateMessage() { double v = value*101d-1d; if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10)))); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); } @Override protected void applyValue() { double v = value*101d-1d; if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10)))); - mod.record_pos_delay = (long) (v*10); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); + mod.record_pos_delay = (long) v; mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); mod.conf.save(); } @@ -220,152 +226,5 @@ public class RepeatingScreen extends Screen { addDrawableChild(export_btn); addDrawableChild(import_btn); addDrawableChild(pos_delay_slider); - - -// 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)); -// was_build = true; -// -// rootComponent.child( -// Containers.horizontalFlow(Sizing.content(), Sizing.content()).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 += "\n"; -// } -// -// 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("\n")) -// 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),-20,100) -// .setFromDiscreteValue(mod.record_pos_delay) -// .message((String s)->{ -// mod.record_pos_delay = Long.parseLong(s); -// mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); -// mod.conf.save(); -// if (mod.record_pos_delay > -1) -// return Text.translatable("text.repeating-mod.pos_delay", s); -// return Text.translatable("text.repeating-mod.nan_pos_delay"); -// }).scrollStep(25) -// .margins(Insets.of(1)) -// .tooltip(Text.translatable("text.repeating-mod.pos_delay_text"))) -// .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/events/RecordBlockBreakEvent.java b/src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java new file mode 100644 index 0000000..9a2d203 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java @@ -0,0 +1,32 @@ +package themixray.repeating.mod.events; + +import net.minecraft.util.math.BlockPos; +import themixray.repeating.mod.RepeatingMod; + +public class RecordBlockBreakEvent extends RecordEvent { + public BlockPos pos; + + public static RecordBlockBreakEvent fromArgs(String[] a) { + return new RecordBlockBreakEvent(new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2]))); + } + + public RecordBlockBreakEvent( + BlockPos pos) { + this.pos = pos; + } + + public void replay() { + RepeatingMod.client.interactionManager.breakBlock(pos); + } + + public String serialize() { + return "b=" + pos.getX() + "&" + pos.getY() + "&" + pos.getZ(); + } + + public String getType() { + return "block_break"; + } +} diff --git a/src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java b/src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java new file mode 100644 index 0000000..b79da8b --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java @@ -0,0 +1,46 @@ +package themixray.repeating.mod.events; + +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.RepeatingMod; + +public class RecordBlockInteractEvent extends RecordEvent { + public Hand hand; + public BlockHitResult hitResult; + + public static RecordBlockInteractEvent fromArgs(String[] a) { + return new RecordBlockInteractEvent( + Hand.valueOf(a[5]), + new BlockHitResult(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Direction.byId(Integer.parseInt(a[4])), + new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2])), + a[3].equals("1"))); + } + + public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { + this.hand = hand; + this.hitResult = hitResult; + } + + public void replay() { + RepeatingMod.client.interactionManager.interactBlock(RepeatingMod.client.player, hand, hitResult); + } + + public String serialize() { + return "i=" + hitResult.getBlockPos().getX() + "&" + hitResult.getBlockPos().getY() + "&" + hitResult.getBlockPos().getZ() + + "&" + (hitResult.isInsideBlock() ? "1" : "0") + "&" + hitResult.getSide().getId() + "&" + hand.name(); + } + + public String getType() { + return "block_interact"; + } +} diff --git a/src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java b/src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java new file mode 100644 index 0000000..2d899b4 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java @@ -0,0 +1,29 @@ +package themixray.repeating.mod.events; + +public class RecordDelayEvent extends RecordEvent { + public long delay; + + public static RecordDelayEvent fromArgs(String[] a) { + return new RecordDelayEvent(Long.parseLong(a[0])); + } + + public RecordDelayEvent(long delay) { + this.delay = delay; + } + + public void replay() { + try { + Thread.sleep(delay / 20 * 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public String serialize() { + return "d=" + delay; + } + + public String getType() { + return "delay"; + } +} diff --git a/src/main/java/themixray/repeating/mod/events/RecordEvent.java b/src/main/java/themixray/repeating/mod/events/RecordEvent.java new file mode 100644 index 0000000..3bf5b9c --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordEvent.java @@ -0,0 +1,28 @@ +package themixray.repeating.mod.events; + +public abstract class RecordEvent { + public abstract void replay(); + public abstract String serialize(); + public abstract String getType(); + + public static RecordEvent deserialize(String t) { + try { + String type = String.valueOf(t.charAt(0)); + String[] args = t.substring(2).split("&"); + if (type.equals("d")) { + return RecordDelayEvent.fromArgs(args); + } else if (type.equals("m")) { + return RecordMoveEvent.fromArgs(args); + } else if (type.equals("p")) { + return RecordInputEvent.fromArgs(args); + } else if (type.equals("b")) { + return RecordBlockBreakEvent.fromArgs(args); + } else if (type.equals("i")) { + return RecordBlockInteractEvent.fromArgs(args); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/themixray/repeating/mod/events/RecordInputEvent.java b/src/main/java/themixray/repeating/mod/events/RecordInputEvent.java new file mode 100644 index 0000000..093d280 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordInputEvent.java @@ -0,0 +1,147 @@ +package themixray.repeating.mod.events; + +import themixray.repeating.mod.RepeatingMod; + +public class RecordInputEvent extends RecordEvent { + public Boolean sneaking; + public Boolean jumping; + public Boolean pressingForward; + public Boolean pressingBack; + public Boolean pressingLeft; + public Boolean pressingRight; + public Boolean sprinting; + + public Float movementSideways; + public Float movementForward; + + public float yaw; + public float head_yaw; + public float body_yaw; + public float pitch; + public float speed; + + public static RecordInputEvent fromArgs(String[] a) { + return new RecordInputEvent( + (a[0].equals("n") ? null : a[0].equals("1")), + (a[1].equals("n") ? null : a[1].equals("1")), + (a[2].equals("n") ? null : Float.parseFloat(a[2])), + (a[3].equals("n") ? null : Float.parseFloat(a[3])), + (a[4].equals("n") ? null : a[4].equals("1")), + (a[5].equals("n") ? null : a[5].equals("1")), + (a[6].equals("n") ? null : a[6].equals("1")), + (a[7].equals("n") ? null : a[7].equals("1")), + Float.parseFloat(a[8]), Float.parseFloat(a[9]), + Float.parseFloat(a[10]), + (a[11].equals("n") ? null : a[11].equals("1")), + Float.parseFloat(a[12]), + Float.parseFloat(a[13])); + } + + public RecordInputEvent(Boolean sneaking, + Boolean jumping, + Float movementSideways, + Float movementForward, + Boolean pressingForward, + Boolean pressingBack, + Boolean pressingLeft, + Boolean pressingRight, + float head_yaw, + float body_yaw, + float head_pitch, + Boolean sprinting, + float yaw, + float speed) { + this.sneaking = sneaking; + this.jumping = jumping; + this.movementSideways = movementSideways; + this.movementForward = movementForward; + this.pressingForward = pressingForward; + this.pressingBack = pressingBack; + this.pressingLeft = pressingLeft; + this.pressingRight = pressingRight; + this.head_yaw = head_yaw; + this.body_yaw = body_yaw; + this.pitch = head_pitch; + this.sprinting = sprinting; + this.yaw = yaw; + this.speed = speed; + } + + public void fillEmpty(RecordInputEvent e) { + if (sneaking == null) sneaking = e.sneaking; + if (jumping == null) jumping = e.jumping; + if (movementSideways == null) movementSideways = e.movementSideways; + if (movementForward == null) movementForward = e.movementForward; + if (pressingForward == null) pressingForward = e.pressingForward; + if (pressingBack == null) pressingBack = e.pressingBack; + if (pressingLeft == null) pressingLeft = e.pressingLeft; + if (pressingRight == null) pressingRight = e.pressingRight; + if (sprinting == null) sprinting = e.sprinting; + } + + public boolean isEmpty() { + return sneaking == null && + jumping == null && + movementSideways == null && + movementForward == null && + pressingForward == null && + pressingBack == null && + pressingLeft == null && + pressingRight == null && + sprinting == null; + } + + public void replay() { + RepeatingMod.input_replay = this; + } + + public void inputCallback() { + if (sprinting != null && RepeatingMod.client.player.isSprinting() != sprinting) + RepeatingMod.client.player.setSprinting(sprinting); + if (RepeatingMod.client.player.getYaw() != yaw) + RepeatingMod.client.player.setYaw(yaw); + if (RepeatingMod.client.player.getHeadYaw() != head_yaw) + RepeatingMod.client.player.setHeadYaw(head_yaw); + if (RepeatingMod.client.player.getBodyYaw() != body_yaw) + RepeatingMod.client.player.setBodyYaw(body_yaw); + if (RepeatingMod.client.player.getPitch() != pitch) + RepeatingMod.client.player.setPitch(pitch); + if (RepeatingMod.client.player.getMovementSpeed() != speed) + RepeatingMod.client.player.setMovementSpeed(speed); + if (sneaking != null && RepeatingMod.client.player.input.sneaking != sneaking) + RepeatingMod.client.player.input.sneaking = sneaking; + if (jumping != null && RepeatingMod.client.player.input.jumping != jumping) + RepeatingMod.client.player.input.jumping = jumping; + if (movementSideways != null && RepeatingMod.client.player.input.movementSideways != movementSideways) + RepeatingMod.client.player.input.movementSideways = movementSideways; + if (movementForward != null && RepeatingMod.client.player.input.movementForward != movementForward) + RepeatingMod.client.player.input.movementForward = movementForward; + if (pressingForward != null && RepeatingMod.client.player.input.pressingForward != pressingForward) + RepeatingMod.client.player.input.pressingForward = pressingForward; + if (pressingBack != null && RepeatingMod.client.player.input.pressingBack != pressingBack) + RepeatingMod.client.player.input.pressingBack = pressingBack; + if (pressingLeft != null && RepeatingMod.client.player.input.pressingLeft != pressingLeft) + RepeatingMod.client.player.input.pressingLeft = pressingLeft; + if (pressingRight != null && RepeatingMod.client.player.input.pressingRight != pressingRight) + RepeatingMod.client.player.input.pressingRight = pressingRight; + } + + public String serialize() { + return "p=" + + ((sneaking == null) ? "n" : (sneaking ? "1" : "0")) + "&" + + ((jumping == null) ? "n" : (jumping ? "1" : "0")) + "&" + + ((movementSideways == null) ? "n" : movementSideways) + "&" + + ((movementForward == null) ? "n" : movementForward) + "&" + + ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")) + "&" + + ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")) + "&" + + ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")) + "&" + + ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")) + "&" + + head_yaw + "&" + body_yaw + "&" + pitch + "&" + + ((sprinting == null) ? "n" : (sprinting ? "1" : "0") + + "&" + yaw + "&" + speed); + } + + public String getType() { + return "input"; + } +} diff --git a/src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java b/src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java new file mode 100644 index 0000000..4adf2f4 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java @@ -0,0 +1,42 @@ +package themixray.repeating.mod.events; + +import net.minecraft.entity.MovementType; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.RepeatingMod; + +public class RecordMoveEvent extends RecordEvent { + public Vec3d vec; + public float yaw; + public float pitch; + + public static RecordMoveEvent fromArgs(String[] a) { + return new RecordMoveEvent(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Float.parseFloat(a[3]), + Float.parseFloat(a[4])); + } + + public RecordMoveEvent(Vec3d vec, float yaw, float pitch) { + this.vec = vec; + this.yaw = yaw; + this.pitch = pitch; + } + + public void replay() { + Vec3d p = RepeatingMod.client.player.getPos(); + Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); + RepeatingMod.client.player.move(MovementType.SELF, v); + RepeatingMod.client.player.setYaw(yaw); + RepeatingMod.client.player.setPitch(pitch); + } + + public String serialize() { + return "m=" + vec.getX() + "&" + vec.getY() + "&" + vec.getZ() + "&" + yaw + "&" + pitch; + } + + public String getType() { + return "move"; + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index 594cbd0..a645ef1 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -9,11 +9,11 @@ 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.events.RecordBlockBreakEvent; +import themixray.repeating.mod.events.RecordBlockInteractEvent; import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.TickTask; -import java.util.ArrayList; - @Mixin(ClientPlayerEntity.class) public abstract class MovementMixin { @@ -21,13 +21,13 @@ public abstract class MovementMixin { private void init(CallbackInfo ci) { PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { if (RepeatingMod.me.is_recording) - RepeatingMod.me.recordTick(new RepeatingMod.RecordBlockBreakEvent(pos)); + RepeatingMod.me.recordTick(new RecordBlockBreakEvent(pos)); }); UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { if (hitResult.getType().equals(HitResult.Type.BLOCK)) if (RepeatingMod.me.is_recording) - RepeatingMod.me.recordTick(new RepeatingMod.RecordBlockInteractEvent(hand,hitResult)); + RepeatingMod.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); return ActionResult.PASS; }); } diff --git a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java new file mode 100644 index 0000000..c116952 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java @@ -0,0 +1,32 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.listener.ServerPlayPacketListener; +import net.minecraft.network.packet.Packet; +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; +import themixray.repeating.mod.TickTask; + +import java.time.Duration; +import java.util.function.BooleanSupplier; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class NetworkMixin { + @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") + private void onSendPacket1Head(Packet packet, + CallbackInfo ci) { + + } + + @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") + private void onSendPacket2Head(Packet packet, + BooleanSupplier sendCondition, + Duration expirationTime, + CallbackInfo ci) { + + } +} diff --git a/src/main/java/themixray/repeating/mod/render/RenderHelper.java b/src/main/java/themixray/repeating/mod/render/RenderHelper.java index edbe614..4f80464 100644 --- a/src/main/java/themixray/repeating/mod/render/RenderHelper.java +++ b/src/main/java/themixray/repeating/mod/render/RenderHelper.java @@ -31,11 +31,11 @@ public class RenderHelper { buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); } - public WorldBuffer startTri(WorldRenderContext context) { + public static WorldBuffer startTri(WorldRenderContext context) { return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context); } - public void endTri(WorldBuffer buffer) { + public static void endTri(WorldBuffer buffer) { //glDepthRange(0, 0.7); glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -54,12 +54,12 @@ public class RenderHelper { buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); } - public void drawRectFromTri(WorldBuffer buffer, - float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - Color color) { + public static void drawRectFromTri(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + Color color) { drawTri(buffer, x1, y1, z1, x2, y2, z2, diff --git a/src/main/java/themixray/repeating/mod/render/RenderSystem.java b/src/main/java/themixray/repeating/mod/render/RenderSystem.java index 2a095f7..a5fa15c 100644 --- a/src/main/java/themixray/repeating/mod/render/RenderSystem.java +++ b/src/main/java/themixray/repeating/mod/render/RenderSystem.java @@ -6,7 +6,7 @@ import themixray.repeating.mod.render.shader.ShaderManager; @UtilityClass public class RenderSystem { - public void init() { + public static void init() { BufferManager.init(); ShaderManager.init(); } From b6589b362992fe1c28c671ebb6d0d641d6e85268 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 21 Apr 2024 15:35:14 +0300 Subject: [PATCH 11/72] screen rewrite what LOL --- .../mod/{RepeatingMod.java => Main.java} | 0 .../mod/{RecordFile.java => RecordState.java} | 30 ++++++---- .../RecordBlockBreakEvent.java | 4 +- .../RecordBlockInteractEvent.java | 4 +- .../{events => event}/RecordDelayEvent.java | 0 .../mod/{events => event}/RecordEvent.java | 0 .../{events => event}/RecordInputEvent.java | 60 +++++++++---------- .../{events => event}/RecordMoveEvent.java | 10 ++-- 8 files changed, 57 insertions(+), 51 deletions(-) rename src/main/java/themixray/repeating/mod/{RepeatingMod.java => Main.java} (100%) rename src/main/java/themixray/repeating/mod/{RecordFile.java => RecordState.java} (78%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordBlockBreakEvent.java (86%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordBlockInteractEvent.java (91%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordDelayEvent.java (100%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordEvent.java (100%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordInputEvent.java (67%) rename src/main/java/themixray/repeating/mod/{events => event}/RecordMoveEvent.java (79%) diff --git a/src/main/java/themixray/repeating/mod/RepeatingMod.java b/src/main/java/themixray/repeating/mod/Main.java similarity index 100% rename from src/main/java/themixray/repeating/mod/RepeatingMod.java rename to src/main/java/themixray/repeating/mod/Main.java diff --git a/src/main/java/themixray/repeating/mod/RecordFile.java b/src/main/java/themixray/repeating/mod/RecordState.java similarity index 78% rename from src/main/java/themixray/repeating/mod/RecordFile.java rename to src/main/java/themixray/repeating/mod/RecordState.java index 5508e16..4a55c68 100644 --- a/src/main/java/themixray/repeating/mod/RecordFile.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -7,6 +7,7 @@ import themixray.repeating.mod.events.RecordEvent; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.ArrayList; import java.util.List; public class RecordFile { @@ -68,37 +69,42 @@ public class RecordFile { } - public void writeToFile(File file) { + public static void readFromMod() { } - public static void readFromMod() { + public void writeToFile(File file) { } public static RecordFile readFromFile(File file) throws IOException { String text = Files.readString(file.toPath()); - List lines = List.of(text.split("\n")); - String last_line = lines.get(lines.size()-1); - lines = lines.subList(0,lines.size()-1); - List + List signature = lines.subList(0,4); - for (String line: lines) - RepeatingMod.me.record.add(RecordEvent.deserialize(line)); + String name = signature.get(0); + String data = signature.get(1); + String author = signature.get(2); - String[] lss0 = ls.split("x"); + String record_pos = signature.get(3); + + String[] lss0 = record_pos.split("x"); String[] lss1 = lss0[0].split("n"); String[] lss2 = lss0[1].split("n"); - RepeatingMod.me.start_record_pos = new Vec3d( + + Vec3d start_record_pos = new Vec3d( Float.parseFloat(lss1[0]), Float.parseFloat(lss1[1]), Float.parseFloat(lss1[2])); - RepeatingMod.me.finish_record_pos = new Vec3d( + Vec3d finish_record_pos = new Vec3d( Float.parseFloat(lss2[0]), Float.parseFloat(lss2[1]), Float.parseFloat(lss2[2])); - RepeatingMod.sendMessage(Text.literal("")); + + List event_lines = lines.subList(4,lines.size()); + List events = event_lines.stream().map(RecordEvent::deserialize).toList(); + + return new RecordFile(file, ) } } diff --git a/src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java b/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java index 9a2d203..82934be 100644 --- a/src/main/java/themixray/repeating/mod/events/RecordBlockBreakEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java @@ -1,7 +1,7 @@ package themixray.repeating.mod.events; import net.minecraft.util.math.BlockPos; -import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.Main; public class RecordBlockBreakEvent extends RecordEvent { public BlockPos pos; @@ -19,7 +19,7 @@ public class RecordBlockBreakEvent extends RecordEvent { } public void replay() { - RepeatingMod.client.interactionManager.breakBlock(pos); + Main.client.interactionManager.breakBlock(pos); } public String serialize() { diff --git a/src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java b/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java similarity index 91% rename from src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java index b79da8b..2d176ed 100644 --- a/src/main/java/themixray/repeating/mod/events/RecordBlockInteractEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java @@ -5,7 +5,7 @@ import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.Main; public class RecordBlockInteractEvent extends RecordEvent { public Hand hand; @@ -32,7 +32,7 @@ public class RecordBlockInteractEvent extends RecordEvent { } public void replay() { - RepeatingMod.client.interactionManager.interactBlock(RepeatingMod.client.player, hand, hitResult); + Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); } public String serialize() { diff --git a/src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java b/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/events/RecordDelayEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java diff --git a/src/main/java/themixray/repeating/mod/events/RecordEvent.java b/src/main/java/themixray/repeating/mod/event/RecordEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/events/RecordEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordEvent.java diff --git a/src/main/java/themixray/repeating/mod/events/RecordInputEvent.java b/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java similarity index 67% rename from src/main/java/themixray/repeating/mod/events/RecordInputEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordInputEvent.java index 093d280..e4cd62a 100644 --- a/src/main/java/themixray/repeating/mod/events/RecordInputEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java @@ -1,6 +1,6 @@ package themixray.repeating.mod.events; -import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.Main; public class RecordInputEvent extends RecordEvent { public Boolean sneaking; @@ -92,38 +92,38 @@ public class RecordInputEvent extends RecordEvent { } public void replay() { - RepeatingMod.input_replay = this; + Main.input_replay = this; } public void inputCallback() { - if (sprinting != null && RepeatingMod.client.player.isSprinting() != sprinting) - RepeatingMod.client.player.setSprinting(sprinting); - if (RepeatingMod.client.player.getYaw() != yaw) - RepeatingMod.client.player.setYaw(yaw); - if (RepeatingMod.client.player.getHeadYaw() != head_yaw) - RepeatingMod.client.player.setHeadYaw(head_yaw); - if (RepeatingMod.client.player.getBodyYaw() != body_yaw) - RepeatingMod.client.player.setBodyYaw(body_yaw); - if (RepeatingMod.client.player.getPitch() != pitch) - RepeatingMod.client.player.setPitch(pitch); - if (RepeatingMod.client.player.getMovementSpeed() != speed) - RepeatingMod.client.player.setMovementSpeed(speed); - if (sneaking != null && RepeatingMod.client.player.input.sneaking != sneaking) - RepeatingMod.client.player.input.sneaking = sneaking; - if (jumping != null && RepeatingMod.client.player.input.jumping != jumping) - RepeatingMod.client.player.input.jumping = jumping; - if (movementSideways != null && RepeatingMod.client.player.input.movementSideways != movementSideways) - RepeatingMod.client.player.input.movementSideways = movementSideways; - if (movementForward != null && RepeatingMod.client.player.input.movementForward != movementForward) - RepeatingMod.client.player.input.movementForward = movementForward; - if (pressingForward != null && RepeatingMod.client.player.input.pressingForward != pressingForward) - RepeatingMod.client.player.input.pressingForward = pressingForward; - if (pressingBack != null && RepeatingMod.client.player.input.pressingBack != pressingBack) - RepeatingMod.client.player.input.pressingBack = pressingBack; - if (pressingLeft != null && RepeatingMod.client.player.input.pressingLeft != pressingLeft) - RepeatingMod.client.player.input.pressingLeft = pressingLeft; - if (pressingRight != null && RepeatingMod.client.player.input.pressingRight != pressingRight) - RepeatingMod.client.player.input.pressingRight = pressingRight; + if (sprinting != null && Main.client.player.isSprinting() != sprinting) + Main.client.player.setSprinting(sprinting); + if (Main.client.player.getYaw() != yaw) + Main.client.player.setYaw(yaw); + if (Main.client.player.getHeadYaw() != head_yaw) + Main.client.player.setHeadYaw(head_yaw); + if (Main.client.player.getBodyYaw() != body_yaw) + Main.client.player.setBodyYaw(body_yaw); + if (Main.client.player.getPitch() != pitch) + Main.client.player.setPitch(pitch); + if (Main.client.player.getMovementSpeed() != speed) + Main.client.player.setMovementSpeed(speed); + if (sneaking != null && Main.client.player.input.sneaking != sneaking) + Main.client.player.input.sneaking = sneaking; + if (jumping != null && Main.client.player.input.jumping != jumping) + Main.client.player.input.jumping = jumping; + if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) + Main.client.player.input.movementSideways = movementSideways; + if (movementForward != null && Main.client.player.input.movementForward != movementForward) + Main.client.player.input.movementForward = movementForward; + if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) + Main.client.player.input.pressingForward = pressingForward; + if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) + Main.client.player.input.pressingBack = pressingBack; + if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) + Main.client.player.input.pressingLeft = pressingLeft; + if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) + Main.client.player.input.pressingRight = pressingRight; } public String serialize() { diff --git a/src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java b/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java similarity index 79% rename from src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java rename to src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java index 4adf2f4..757d58b 100644 --- a/src/main/java/themixray/repeating/mod/events/RecordMoveEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java @@ -2,7 +2,7 @@ package themixray.repeating.mod.events; import net.minecraft.entity.MovementType; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.Main; public class RecordMoveEvent extends RecordEvent { public Vec3d vec; @@ -25,11 +25,11 @@ public class RecordMoveEvent extends RecordEvent { } public void replay() { - Vec3d p = RepeatingMod.client.player.getPos(); + Vec3d p = Main.client.player.getPos(); Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); - RepeatingMod.client.player.move(MovementType.SELF, v); - RepeatingMod.client.player.setYaw(yaw); - RepeatingMod.client.player.setPitch(pitch); + Main.client.player.move(MovementType.SELF, v); + Main.client.player.setYaw(yaw); + Main.client.player.setPitch(pitch); } public String serialize() { From 38dad100483cffa060c3dedac36c5198a93b131f Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 00:25:43 +0300 Subject: [PATCH 12/72] fix records list and add btns --- gradle.properties | 8 +- .../java/themixray/repeating/mod/Main.java | 167 +++++++------ .../themixray/repeating/mod/RecordList.java | 74 +++++- .../themixray/repeating/mod/RecordState.java | 108 ++++++-- .../repeating/mod/RenderListener.java | 11 + .../repeating/mod/RepeatingScreen.java | 231 +++++++----------- .../mod/event/RecordBlockBreakEvent.java | 2 +- .../mod/event/RecordBlockInteractEvent.java | 2 +- .../repeating/mod/event/RecordDelayEvent.java | 2 +- .../repeating/mod/event/RecordEvent.java | 2 +- .../repeating/mod/event/RecordInputEvent.java | 2 +- .../repeating/mod/event/RecordMoveEvent.java | 2 +- .../repeating/mod/mixin/ClientMixin.java | 6 +- .../repeating/mod/mixin/EntityMixin.java | 13 +- .../repeating/mod/mixin/InputMixin.java | 8 +- .../repeating/mod/mixin/MovementMixin.java | 14 +- .../repeating/mod/mixin/NetworkMixin.java | 3 - .../repeating/mod/mixin/RendererMixin.java | 2 - .../mod/render/buffer/BufferManager.java | 12 +- .../mod/widget/RecordListWidget.java | 150 ++++++++++++ .../repeating/mod/widget/RecordWidget.java | 192 +++++++++++++++ .../assets/repeating-mod/lang/en_us.json | 13 +- .../assets/repeating-mod/lang/ru_ru.json | 18 +- src/main/resources/fabric.mod.json | 2 +- 24 files changed, 758 insertions(+), 286 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/RenderListener.java create mode 100644 src/main/java/themixray/repeating/mod/widget/RecordListWidget.java create mode 100644 src/main/java/themixray/repeating/mod/widget/RecordWidget.java diff --git a/gradle.properties b/gradle.properties index 9109b7d..a80352b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,12 +4,12 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.20 -yarn_mappings=1.20+build.1 -loader_version=0.14.23 +minecraft_version=1.20.1 +yarn_mappings=1.20.1+build.10 +loader_version=0.15.10 #Fabric api -fabric_version=0.83.0+1.20 +fabric_version=0.92.1+1.20.1 # Mod Properties mod_version = 1.0.6 diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/themixray/repeating/mod/Main.java index 151c8af..9eb4e53 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/themixray/repeating/mod/Main.java @@ -1,6 +1,5 @@ package themixray.repeating.mod; -import com.google.common.collect.Lists; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; @@ -16,28 +15,29 @@ import net.minecraft.util.math.Vec3d; import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import themixray.repeating.mod.events.RecordDelayEvent; -import themixray.repeating.mod.events.RecordEvent; -import themixray.repeating.mod.events.RecordInputEvent; -import themixray.repeating.mod.events.RecordMoveEvent; +import themixray.repeating.mod.event.RecordDelayEvent; +import themixray.repeating.mod.event.RecordEvent; +import themixray.repeating.mod.event.RecordInputEvent; +import themixray.repeating.mod.event.RecordMoveEvent; import themixray.repeating.mod.render.RenderHelper; import themixray.repeating.mod.render.RenderSystem; import themixray.repeating.mod.render.buffer.WorldBuffer; import java.awt.*; +import java.io.File; +import java.io.IOException; import java.util.*; import java.util.List; -public class RepeatingMod implements ClientModInitializer { +public class Main implements ClientModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); public static final MinecraftClient client = MinecraftClient.getInstance(); public static final FabricLoader loader = FabricLoader.getInstance(); - public static RepeatingMod me; + public static Main me; - public Vec3d start_record_pos = null; - public Vec3d finish_record_pos = null; + public RecordList record_list; + public RecordState now_record; - public List record = new ArrayList<>(); public boolean is_recording = false; public long last_record = -1; public TickTask move_tick = null; @@ -59,52 +59,30 @@ public class RepeatingMod implements ClientModInitializer { public static Random rand = new Random(); public EasyConfig conf; + public File records_folder; @Override public void onInitializeClient() { LOGGER.info("Repeating mod initialized"); me = this; + now_record = null; + + records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); + if (!records_folder.exists()) records_folder.mkdir(); + + record_list = new RecordList(records_folder); + record_list.loadRecords(); + RenderSystem.init(); WorldRenderEvents.LAST.register(context -> { WorldBuffer buffer = RenderHelper.startTri(context); - if (start_record_pos != null) { - RenderHelper.drawRectFromTri(buffer, - (float) start_record_pos.getX() - 0.25F, - (float) start_record_pos.getY() + 0.01F, - (float) start_record_pos.getZ() - 0.25F, + if (now_record != null) { + Vec3d start_pos = now_record.getStartRecordPos(); + Vec3d finish_pos = now_record.getFinishRecordPos(); - (float) start_record_pos.getX() + 0.25F, - (float) start_record_pos.getY() + 0.01F, - (float) start_record_pos.getZ() - 0.25F, - - (float) start_record_pos.getX() + 0.25F, - (float) start_record_pos.getY() + 0.01F, - (float) start_record_pos.getZ() + 0.25F, - - (float) start_record_pos.getX() - 0.25F, - (float) start_record_pos.getY() + 0.01F, - (float) start_record_pos.getZ() + 0.25F, - new Color(70,230,70,128)); - } - if (finish_record_pos != null) { - RenderHelper.drawRectFromTri(buffer, - (float) finish_record_pos.getX() - 0.25F, - (float) finish_record_pos.getY() + 0.01F, - (float) finish_record_pos.getZ() - 0.25F, - - (float) finish_record_pos.getX() + 0.25F, - (float) finish_record_pos.getY() + 0.01F, - (float) finish_record_pos.getZ() - 0.25F, - - (float) finish_record_pos.getX() + 0.25F, - (float) finish_record_pos.getY() + 0.01F, - (float) finish_record_pos.getZ() + 0.25F, - - (float) finish_record_pos.getX() - 0.25F, - (float) finish_record_pos.getY() + 0.01F, - (float) finish_record_pos.getZ() + 0.25F, - new Color(230,70,70,128)); + if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128)); + if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128)); } RenderHelper.endTri(buffer); }); @@ -131,11 +109,13 @@ public class RepeatingMod implements ClientModInitializer { if (menu_key.wasPressed()) client.setScreen(menu); if (toggle_replay_key.wasPressed()) { - if (!is_recording) { - if (is_replaying) - stopReplay(); - else startReplay(); - menu.update_btns(); + if (now_record != null) { + if (!is_recording) { + if (is_replaying) + stopReplay(); + else startReplay(); + menu.updateButtons(); + } } } if (toggle_record_key.wasPressed()) { @@ -143,7 +123,7 @@ public class RepeatingMod implements ClientModInitializer { if (is_recording) stopRecording(); else startRecording(); - menu.update_btns(); + menu.updateButtons(); } } }); @@ -154,26 +134,43 @@ public class RepeatingMod implements ClientModInitializer { living_ticks++; } }; + + System.setProperty("java.awt.headless", "false"); } - public RecordEvent getLastRecord(String t) { - for (RecordEvent r:Lists.reverse(new ArrayList<>(record))) { - if (r.getType().equals(t)) { - return r; - } - } - return null; + public void setNowRecord(RecordState record) { + now_record = record; + } + + public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { + RenderHelper.drawRectFromTri(buffer, + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + color); } public void startRecording() { is_recording = true; - menu.update_btns(); - record.clear(); + menu.updateButtons(); - finish_record_pos = null; - Vec3d v = client.player.getPos(); - record.add(new RecordMoveEvent(v,client.player.getHeadYaw(),client.player.getPitch())); - start_record_pos = v; + now_record = record_list.newRecord(); + + Vec3d start_pos = client.player.getPos(); + now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); + now_record.setStartRecordPos(start_pos); if (record_pos_delay > 0) { move_tick = new TickTask( @@ -181,7 +178,7 @@ public class RepeatingMod implements ClientModInitializer { record_pos_delay) { @Override public void run() { - record.add(new RecordMoveEvent(client.player.getPos(), + now_record.addEvent(new RecordMoveEvent(client.player.getPos(), client.player.getHeadYaw(), client.player.getPitch())); } }; @@ -195,15 +192,20 @@ public class RepeatingMod implements ClientModInitializer { long now = living_ticks; if (last_record != -1) { long diff = now - last_record - 2; - if (diff > 0) record.add(new RecordDelayEvent(diff)); + if (diff > 0) now_record.addEvent(new RecordDelayEvent(diff)); } - record.add(e); + now_record.addEvent(e); last_record = now; } } public void recordAllInput() { - RecordInputEvent l = ((RecordInputEvent)getLastRecord("input")); + if (client.player == null) { + stopRecording(); + return; + } + + RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input")); if (l == null) { RecordInputEvent e = new RecordInputEvent( client.player.input.sneaking, @@ -231,7 +233,7 @@ public class RepeatingMod implements ClientModInitializer { ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, - client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(), + client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), client.player.getYaw(),client.player.getMovementSpeed()); @@ -248,12 +250,17 @@ public class RepeatingMod implements ClientModInitializer { public void stopRecording() { is_recording = false; - finish_record_pos = client.player.getPos(); - if (move_tick != null) { + now_record.setFinishRecordPos(client.player.getPos()); + try { + now_record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (move_tick != null) { move_tick.cancel(); move_tick = null; } - menu.update_btns(); + menu.updateButtons(); last_record = -1; sendMessage(Text.translatable("message.repeating-mod.record_stop")); } @@ -262,14 +269,17 @@ public class RepeatingMod implements ClientModInitializer { public void startReplay() { is_recording = false; is_replaying = true; - menu.update_btns(); + menu.updateButtons(); + + List events = now_record.getEvents(); + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { public int replay_index = 0; @Override public void run() { if (!is_replaying) cancel(); - RecordEvent e = record.get(replay_index); + RecordEvent e = events.get(replay_index); if (e instanceof RecordDelayEvent) { setDelay(((RecordDelayEvent) e).delay); } else { @@ -278,11 +288,11 @@ public class RepeatingMod implements ClientModInitializer { replay_index++; if (!loop_replay) { - if (replay_index == record.size()) { + if (replay_index == events.size()) { stopReplay(); cancel(); } - } else if (replay_index == record.size()) { + } else if (replay_index == events.size()) { replay_index = 0; } } @@ -298,7 +308,8 @@ public class RepeatingMod implements ClientModInitializer { replay_tick.cancel(); replay_tick = null; } - menu.update_btns(); + menu.updateButtons(); + record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); sendMessage(Text.translatable("message.repeating-mod.replay_stop")); } diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index 907989f..b1a3c1d 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -1,5 +1,77 @@ package themixray.repeating.mod; -public class RecordList { +import net.minecraft.text.Text; +import themixray.repeating.mod.widget.RecordListWidget; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class RecordList { + private final File folder; + private List records; + private RecordListWidget widget; + + public RecordList(File folder) { + this.folder = folder; + this.records = new ArrayList<>(); + this.widget = new RecordListWidget(0, 0, 120, 80); + } + + public List getRecords() { + return records; + } + + public File getFolder() { + return folder; + } + + public RecordListWidget getWidget() { + return widget; + } + + public void loadRecords() { + for (File file : folder.listFiles()) { + try { + addRecord(file); + } catch (Exception e) {} + } + } + + public void addRecord(File file) throws Exception { + addRecord(RecordState.load(file)); + } + + public void addRecord(RecordState record) { + records.add(record); + widget.addWidget(record); + } + + public void removeRecord(RecordState record) { + records.remove(record); + widget.removeWidget(record); + } + + public RecordState newRecord() { + Date date = new Date(); + String name = "Unnamed"; + String author = Main.client.player.getName().getString(); + + File file = new File(Main.me.records_folder, + "record_" + RecordState.FILE_DATE_FORMAT.format(date) + + "_" + Main.rand.nextInt(10) + ".rrm"); + + RecordState state = new RecordState( + file, name, date, author, + new ArrayList<>(), + null, + null); + + addRecord(state); + + return state; + } } diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index 4a55c68..fe844c8 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -1,32 +1,38 @@ package themixray.repeating.mod; -import net.minecraft.text.Text; +import com.google.common.collect.Lists; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.events.RecordEvent; +import themixray.repeating.mod.event.RecordEvent; +import java.awt.*; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; -public class RecordFile { +public class RecordState { + public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM.dd.yyyy HH:mm:ss"); + public static SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy_HH-mm-ss"); + private final File file; private String name; - private String date; + private Date date; private String author; private List events; private Vec3d start_record_pos; private Vec3d finish_record_pos; - public RecordFile(File file, - String name, - String date, - String author, - List events, - Vec3d start_record_pos, - Vec3d finish_record_pos) { + public RecordState(File file, + String name, + Date date, + String author, + List events, + Vec3d start_record_pos, + Vec3d finish_record_pos) { this.file = file; this.name = name; this.date = date; @@ -49,15 +55,27 @@ public class RecordFile { return author; } - public String getDate() { + public Date getDate() { return date; } + public List getEvents() { + return events; + } + + public Vec3d getFinishRecordPos() { + return finish_record_pos; + } + + public Vec3d getStartRecordPos() { + return start_record_pos; + } + public void setAuthor(String author) { this.author = author; } - public void setDate(String date) { + public void setDate(Date date) { this.date = date; } @@ -65,26 +83,66 @@ public class RecordFile { this.name = name; } - public void writeToMod() { - + public void setEvents(List events) { + this.events = events; } - public static void readFromMod() { - + public void setFinishRecordPos(Vec3d finish_record_pos) { + this.finish_record_pos = finish_record_pos; } - public void writeToFile(File file) { - + public void setStartRecordPos(Vec3d start_record_pos) { + this.start_record_pos = start_record_pos; } - public static RecordFile readFromFile(File file) throws IOException { + public void addEvent(RecordEvent event) { + events.add(event); + } + + public RecordEvent getLastEvent(String type) { + for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { + if (r.getType().equals(type)) { + return r; + } + } + return null; + } + + public void save() throws IOException { + StringBuilder text = new StringBuilder(); + + text.append(name).append("\n") + .append(DATE_FORMAT.format(date)).append("\n") + .append(author).append("\n"); + + text.append(start_record_pos.getX()).append("n") + .append(start_record_pos.getY()).append("n") + .append(start_record_pos.getZ()).append("x") + .append(finish_record_pos.getX()).append("n") + .append(finish_record_pos.getY()).append("n") + .append(finish_record_pos.getZ()); + + for (int i = 0; i < events.size(); i++) { + text.append("\n"); + text.append(events.get(i).serialize()); + } + + Files.write(file.toPath(), text.toString().getBytes()); + + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + desktop.browse(file.toURI()); + } + } + + public static RecordState load(File file) throws Exception { String text = Files.readString(file.toPath()); List lines = List.of(text.split("\n")); List signature = lines.subList(0,4); String name = signature.get(0); - String data = signature.get(1); + Date date = DATE_FORMAT.parse(signature.get(1)); String author = signature.get(2); String record_pos = signature.get(3); @@ -105,6 +163,12 @@ public class RecordFile { List event_lines = lines.subList(4,lines.size()); List events = event_lines.stream().map(RecordEvent::deserialize).toList(); - return new RecordFile(file, ) + return new RecordState(file, name, date, author, events, start_record_pos, finish_record_pos); + } + + public void remove() { + file.delete(); + Main.me.record_list.removeRecord(this); + Main.me.record_list.getWidget().removeWidget(this); } } diff --git a/src/main/java/themixray/repeating/mod/RenderListener.java b/src/main/java/themixray/repeating/mod/RenderListener.java new file mode 100644 index 0000000..9992e48 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/RenderListener.java @@ -0,0 +1,11 @@ +package themixray.repeating.mod; + +import net.minecraft.client.gui.DrawContext; + +public interface RenderListener { + default boolean beforeRender() { + return true; + } + + void render(DrawContext context, int mouseX, int mouseY, float delta); +} diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 6679c99..ed3ccb8 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -2,182 +2,99 @@ package themixray.repeating.mod; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.events.RecordEvent; -import javax.swing.*; import java.awt.*; import java.io.File; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.util.ArrayList; import java.util.List; @Environment(EnvType.CLIENT) public class RepeatingScreen extends Screen { - protected RepeatingScreen() { - super(Text.literal("")); - this.mod = RepeatingMod.me; - } - - public RepeatingMod mod; + private static List render_listeners = new ArrayList<>(); public ButtonWidget record_btn; - public ButtonWidget replay_btn; public ButtonWidget loop_btn; - - public ButtonWidget export_btn; public ButtonWidget import_btn; public SliderWidget pos_delay_slider; public boolean was_build = false; - public void update_btns() { + protected RepeatingScreen() { + super(Text.empty()); + } + + public static void addRenderListener(RenderListener render) { + render_listeners.add(render); + } + + public static void removeRenderListener(RenderListener render) { + render_listeners.remove(render); + } + + public void updateButtons() { if (was_build) { - replay_btn.setMessage(Text.translatable("text.repeating-mod." + - ((mod.is_replaying) ? "stop_replay" : "start_replay"))); - record_btn.setMessage(Text.translatable("text.repeating-mod." + - ((mod.is_recording) ? "stop_record" : "start_record"))); - loop_btn.setMessage(Text.of(((mod.loop_replay) ? "\uefff " : "\ueffe "))); + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); + loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); } } @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { renderBackground(context); + + for (RenderListener l : render_listeners) { + if (l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } + super.render(context, mouseX, mouseY, delta); + + for (RenderListener l : render_listeners) { + if (!l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } } @Override protected void init() { record_btn = ButtonWidget.builder( Text.translatable("text.repeating-mod.start_record"), button -> { - if (!mod.is_replaying) { - if (mod.is_recording) - mod.stopRecording(); - else mod.startRecording(); - update_btns(); + if (!Main.me.is_replaying) { + if (Main.me.is_recording) + Main.me.stopRecording(); + else Main.me.startRecording(); + updateButtons(); } }) - .dimensions(width / 2 - 60, height / 2 - 54, 120, 20) + .dimensions(width / 2 - 60, height / 2 - 76, 120, 20) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) .build(); - replay_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.start_replay"), button -> { - if (!mod.is_recording) { - if (mod.is_replaying) - mod.stopReplay(); - else mod.startReplay(); - update_btns(); - } + loop_btn = ButtonWidget.builder(Text.empty(), button -> { + Main.me.loop_replay = !Main.me.loop_replay; + updateButtons(); }) - .dimensions(width / 2 - 60, height / 2 - 32, 98, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))) - .build(); - - loop_btn = ButtonWidget.builder(Text.of(""), button -> { - mod.loop_replay = !mod.loop_replay; - update_btns(); - }) - .dimensions(width / 2 + 40, height / 2 - 32, 20, 20) + .dimensions(width / 2 - 60, height / 2 - 54, 120, 20) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) .build(); - export_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.export"), button -> { - if (mod.finish_record_pos == null) return; - StringBuilder t = new StringBuilder(); - for (int i = 0; i < mod.record.size(); i++) { - t.append(mod.record.get(i).serialize()); - t.append("\n"); - } - t.append(mod.start_record_pos.getX()+"n"+ - mod.start_record_pos.getY()+"n"+ - mod.start_record_pos.getZ()+"x"+ - mod.finish_record_pos.getX()+"n"+ - mod.finish_record_pos.getY()+"n"+ - mod.finish_record_pos.getZ()); - - File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); - if (!p.exists()) p.mkdir(); - File file = new File(p,"export_"+ - new SimpleDateFormat("MM_dd_yyyy").format(new Date()) - +"_"+RepeatingMod.rand.nextInt(10)+".txt"); - try { - if (!file.exists()) file.createNewFile(); - Files.write(file.toPath(), t.toString().getBytes()); - Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\""); - } catch (Exception e) { - e.printStackTrace(); - } - }) - .dimensions(width / 2 - 60, height / 2 - 10, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.export_tooltip"))) - .build(); - - import_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.import"), button -> { - mod.record.clear(); - - File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating"); - if (!p.exists()) p.mkdir(); - - JFileChooser fc = new JFileChooser() { - @Override - protected JDialog createDialog(Component parent) throws HeadlessException { - JDialog dialog = super.createDialog(parent); - dialog.setLocationByPlatform(true); - dialog.setAlwaysOnTop(true); - return dialog; - } - - }; - - int retValue = fc.showOpenDialog(null); - if (retValue == JFileChooser.APPROVE_OPTION){ - File file = fc.getSelectedFile(); - try { - String t = Files.readString(file.toPath()); - List ss = List.of(t.split("\n")); - String ls = ss.get(ss.size()-1); - ss = ss.subList(0,ss.size()-1); - for (String s:ss) - mod.record.add(RecordEvent.deserialize(s)); - String[] lss0 = ls.split("x"); - String[] lss1 = lss0[0].split("n"); - String[] lss2 = lss0[1].split("n"); - mod.start_record_pos = new Vec3d( - Float.parseFloat(lss1[0]), - Float.parseFloat(lss1[1]), - Float.parseFloat(lss1[2])); - mod.finish_record_pos = new Vec3d( - Float.parseFloat(lss2[0]), - Float.parseFloat(lss2[1]), - Float.parseFloat(lss2[2])); - RepeatingMod.sendMessage(Text.literal("")); - } catch (Exception e) { - e.printStackTrace(); - } - } - }) - .dimensions(width / 2 - 60, height / 2 + 12, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) - .build(); - pos_delay_slider = new SliderWidget( - width / 2 - 60, height / 2 + 34, 120, 20, - (mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(mod.record_pos_delay)), - (mod.record_pos_delay+1d)/101d) { + width / 2 - 60, height / 2 - 32, 120, 20, + (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), + (Main.me.record_pos_delay+1d)/101d) { @Override protected void updateMessage() { @@ -191,9 +108,9 @@ public class RepeatingScreen extends Screen { double v = value*101d-1d; if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - mod.record_pos_delay = (long) v; - mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); - mod.conf.save(); + Main.me.record_pos_delay = (long) v; + Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); + Main.me.conf.save(); } @Override @@ -216,15 +133,57 @@ public class RepeatingScreen extends Screen { }; pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); + import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { + new Thread(() -> { + FileDialog fd = new FileDialog((java.awt.Frame) null); + fd.setMultipleMode(true); + fd.setName("Choose record files"); + fd.setTitle("Choose record files"); + fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); + fd.setVisible(true); + + File[] files = fd.getFiles(); + if (files != null) { + for (File file : files) { + try { + Main.me.record_list.addRecord(file); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }}).start(); + }) + .dimensions(width / 2 - 60, height / 2 - 10, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) + .build(); + + Main.me.record_list.getWidget().setX(width / 2 - Main.me.record_list.getWidget().getWidth() / 2); + Main.me.record_list.getWidget().setY(height / 2 + 12); + Main.me.record_list.getWidget().init(this); + was_build = true; - update_btns(); + updateButtons(); - addDrawableChild(replay_btn); addDrawableChild(loop_btn); addDrawableChild(record_btn); - addDrawableChild(export_btn); addDrawableChild(import_btn); addDrawableChild(pos_delay_slider); } + + public T addDrawableChild(T drawableElement) { + return super.addDrawableChild(drawableElement); + } + + public T addDrawable(T drawable) { + return super.addDrawable(drawable); + } + + public T addSelectableChild(T child) { + return super.addSelectableChild(child); + } + + public void remove(Element child) { + super.remove(child); + } } diff --git a/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java b/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java index 82934be..a39380c 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; import net.minecraft.util.math.BlockPos; import themixray.repeating.mod.Main; diff --git a/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java b/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java index 2d176ed..59d0b76 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; diff --git a/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java b/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java index 2d899b4..7e4bcca 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; public class RecordDelayEvent extends RecordEvent { public long delay; diff --git a/src/main/java/themixray/repeating/mod/event/RecordEvent.java b/src/main/java/themixray/repeating/mod/event/RecordEvent.java index 3bf5b9c..e7c2ffe 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; public abstract class RecordEvent { public abstract void replay(); diff --git a/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java b/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java index e4cd62a..a760440 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; import themixray.repeating.mod.Main; diff --git a/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java b/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java index 757d58b..7b964de 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.events; +package themixray.repeating.mod.event; import net.minecraft.entity.MovementType; import net.minecraft.util.math.Vec3d; diff --git a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java index 1d077ac..60e9e64 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java @@ -5,15 +5,15 @@ 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; +import themixray.repeating.mod.Main; import themixray.repeating.mod.TickTask; @Mixin(MinecraftClient.class) public abstract class ClientMixin { @Inject(at = @At(value = "HEAD"), method = "tick") private void onTickHead(CallbackInfo ci) { - if (RepeatingMod.me.is_recording) - RepeatingMod.me.recordAllInput(); + if (Main.me.is_recording) + Main.me.recordAllInput(); TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); } diff --git a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java index 94cfeb7..8d45d11 100644 --- a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java @@ -6,8 +6,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import themixray.repeating.mod.RepeatingMod; -import themixray.repeating.mod.TickTask; +import themixray.repeating.mod.Main; import java.util.UUID; @@ -17,11 +16,11 @@ public abstract class EntityMixin { @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) private void onSprint(boolean sprinting,CallbackInfo ci) { - if (getUuid().equals(RepeatingMod.client.player.getUuid())) { - if (RepeatingMod.me.is_replaying) { - if (RepeatingMod.input_replay != null && - RepeatingMod.input_replay.sprinting != null && - RepeatingMod.input_replay.sprinting != sprinting) { + if (getUuid().equals(Main.client.player.getUuid())) { + if (Main.me.is_replaying) { + if (Main.input_replay != null && + Main.input_replay.sprinting != null && + Main.input_replay.sprinting != sprinting) { ci.cancel(); return; } diff --git a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java index bcaf1e0..800e147 100644 --- a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java @@ -5,15 +5,15 @@ 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; +import themixray.repeating.mod.Main; @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) { - if (RepeatingMod.input_replay != null) { - RepeatingMod.input_replay.inputCallback(); + if (Main.me.is_replaying) { + if (Main.input_replay != null) { + Main.input_replay.inputCallback(); } } } diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index a645ef1..45190ba 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -9,9 +9,9 @@ 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.events.RecordBlockBreakEvent; -import themixray.repeating.mod.events.RecordBlockInteractEvent; -import themixray.repeating.mod.RepeatingMod; +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordBlockBreakEvent; +import themixray.repeating.mod.event.RecordBlockInteractEvent; import themixray.repeating.mod.TickTask; @Mixin(ClientPlayerEntity.class) @@ -20,14 +20,14 @@ public abstract class MovementMixin { @Inject(at = @At(value = "HEAD"), method = "init") private void init(CallbackInfo ci) { PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { - if (RepeatingMod.me.is_recording) - RepeatingMod.me.recordTick(new RecordBlockBreakEvent(pos)); + if (Main.me.is_recording) + Main.me.recordTick(new RecordBlockBreakEvent(pos)); }); UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { if (hitResult.getType().equals(HitResult.Type.BLOCK)) - if (RepeatingMod.me.is_recording) - RepeatingMod.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); + if (Main.me.is_recording) + Main.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); return ActionResult.PASS; }); } diff --git a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java index c116952..1ffb3c6 100644 --- a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java @@ -1,6 +1,5 @@ package themixray.repeating.mod.mixin; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.listener.ServerPlayPacketListener; import net.minecraft.network.packet.Packet; @@ -8,8 +7,6 @@ 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; -import themixray.repeating.mod.TickTask; import java.time.Duration; import java.util.function.BooleanSupplier; diff --git a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java index fcaca78..edb363d 100644 --- a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java @@ -1,12 +1,10 @@ package themixray.repeating.mod.mixin; -import net.minecraft.client.input.KeyboardInput; import net.minecraft.client.render.*; 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; import themixray.repeating.mod.TickTask; @Mixin(GameRenderer.class) diff --git a/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java b/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java index 4c3ae98..76d2853 100644 --- a/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java +++ b/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java @@ -21,28 +21,28 @@ public class BufferManager { }); } - public void bindBuffer() { + public static void bindBuffer() { glBindBuffer(GL_ARRAY_BUFFER, vbo); } - public void unbindBuffer() { + public static void unbindBuffer() { glBindBuffer(GL_ARRAY_BUFFER, 0); } - public void writeBuffer(FloatBuffer buffer) { + public static void writeBuffer(FloatBuffer buffer) { glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); } - public void draw(int drawMode, int verts) { + public static void draw(int drawMode, int verts) { glDrawArrays(drawMode, 0, verts); } - public void bind() { + public static void bind() { prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING); glBindVertexArray(vao); } - public void unbind() { + public static void unbind() { glBindVertexArray(prevVao); } } diff --git a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java new file mode 100644 index 0000000..3106ae5 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java @@ -0,0 +1,150 @@ +package themixray.repeating.mod.widget; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.*; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import themixray.repeating.mod.Main; +import themixray.repeating.mod.RecordState; +import themixray.repeating.mod.RepeatingScreen; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class RecordListWidget extends ScrollableWidget { + private List widgets = new ArrayList<>(); + private boolean focused = false; + + public RecordListWidget(int x, int y, int width, int height) { + super(x,y,width,height,Text.empty()); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + focused = true; + boolean res = super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + focused = false; + return res; + } + + @Override + protected double getDeltaYPerScroll() { + return 10; + } + + @Override + protected void renderContents(DrawContext ctx, int mouseX, int mouseY, float delta) { + int y = 0; + for (RecordWidget wid: widgets) { + wid.setY(y); + wid.render(ctx, mouseX, (int) (mouseY + this.getScrollY()), delta); + + y += wid.getHeight(); + y += 2; + } + } + + public void addWidget(RecordState record) { + RecordWidget widget = new RecordWidget(0, 0, width, 50, record, this); + widget.init(null); + widgets.add(0, widget); + } + + public void removeWidget(RecordState record) { + widgets.removeIf(i -> i.getRecord().equals(record)); + } + + @Override + public void setFocused(boolean focused) { + + } + + @Override + public boolean isFocused() { + return focused; + } + + @Override + protected int getContentsHeight() { + return !widgets.isEmpty() ? widgets.size() * 50 + (widgets.size() - 1) * 2 : 0; + } + + public void init(RepeatingScreen screen) { + for (RecordWidget widget : widgets) { + widget.init(screen); + } + + screen.addDrawableChild(this); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + + } + + public RecordWidget getWidget(RecordState record) { + for (RecordWidget widget : widgets) { + if (widget.getRecord().equals(record)) { + return widget; + } + } + return null; + } + + public interface transport { + boolean check(ClickableWidget ch); + } + + public boolean checkTransport(transport tr) { + for (RecordWidget wid : widgets) { + for (ClickableWidget child : wid.getChildren()) { + if (tr.check(child)) { + return true; + } + } + } + return false; + } + + public boolean checkTransportNF(transport tr) { + for (RecordWidget wid : widgets) { + for (ClickableWidget child : wid.getChildren()) { + boolean res = tr.check(child); + + if (res) { + child.setFocused(true); + return true; + } else { + child.setFocused(false); + } + } + } + return false; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return checkTransportNF((c) -> c.mouseClicked(mouseX, mouseY + this.getScrollY(), button)) || super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + return checkTransport((c) -> c.charTyped(chr, modifiers)) || super.charTyped(chr, modifiers); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return checkTransport((c) -> c.keyPressed(keyCode, scanCode, modifiers)) || super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + return checkTransport((c) -> c.keyReleased(keyCode, scanCode, modifiers)) || super.keyReleased(keyCode, scanCode, modifiers); + } +} diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java new file mode 100644 index 0000000..d8a2d21 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -0,0 +1,192 @@ +package themixray.repeating.mod.widget; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.*; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import themixray.repeating.mod.Main; +import themixray.repeating.mod.RecordState; +import themixray.repeating.mod.RenderListener; +import themixray.repeating.mod.RepeatingScreen; + +import java.awt.*; +import java.io.IOException; +import java.util.ArrayList; + +import java.util.List; +import java.util.function.Consumer; + +public class RecordWidget implements Drawable, Widget { + private RecordState record; + + private List children; + + private RecordListWidget parent; + + private int x; + private int y; + private int width; + private int height; + + public RecordWidget(int x, int y, int width, int height, RecordState record, RecordListWidget parent) { + this.parent = parent; + this.record = record; + + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.children = new ArrayList<>(); + } + + public void setX(int x) { + this.x = x; + } + public void setY(int y) { + this.y = y; + } + public int getX() { + return x; + } + public int getY() { + return y; + } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + + public List getChildren() { + return children; + } + + @Override + public void forEachChild(Consumer consumer) { + children.forEach(consumer); + } + + public void init(RepeatingScreen screen) { + this.children = new ArrayList<>(); + + TextFieldWidget name_widget = new TextFieldWidget( + Main.client.textRenderer, + parent.getX() + getX() + 5, + parent.getY() + getY() + 5, + 70, + 10, + Text.empty()); + + name_widget.setText(record.getName()); + + name_widget.setChangedListener((s) -> { + record.setName(s); + try { + record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + children.add(name_widget); + + ButtonWidget delete_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.delete"), (i) -> { + record.remove(); + }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4, 38, 13).build(); + + children.add(delete_button); + + ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> { + if (Desktop.isDesktopSupported()) { + Desktop desk = Desktop.getDesktop(); + try { + desk.browse(record.getFile().toURI()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 14, 38, 13).build(); + + children.add(export_button); + + ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.start"), (i) -> { + if (Main.me.is_replaying) { + Main.me.stopReplay(); + } + + i.setMessage(Text.translatable("text.repeating-mod.stop")); + Main.me.now_record = record; + Main.me.startReplay(); + }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 28, 38, 13) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); + + children.add(replay_button); + } + + public RecordState getRecord() { + return record; + } + + public void drawText(int x, int y, DrawContext ctx, List lines, float size, int line_height, boolean shadow) { + ctx.getMatrices().push(); + ctx.getMatrices().scale(size, size, size); + + int now_y = y; + + for (Text line : lines) { + ctx.drawText(Main.client.textRenderer, line, (int) (x / size), (int) (now_y / size), line.getStyle().getColor().getRgb(), shadow); + now_y += line_height; + } + + ctx.getMatrices().pop(); + } + + @Override + public void render(DrawContext ctx, int mouseX, int mouseY, float delta) { + int color = record.equals(Main.me.now_record) ? 0xFF555555 : 0xFF333333; + + ctx.fill(parent.getX() + getX(), + parent.getY() + getY(), + parent.getX() + getX() + getWidth(), + parent.getY() + getY() + getHeight(), + color); + + drawText( + parent.getX() + getX() + 5, + parent.getY() + getY() + 5 + 12, + ctx, List.of( + Text.translatable("text.repeating-mod.recorded_at") + .append(": ") + .styled((s) -> s.withColor(0xbbbbbb)), + Text.literal(RecordState.DATE_FORMAT.format(record.getDate())).styled((s) -> s.withColor(0xffffff)), + Text.translatable("text.repeating-mod.author") + .append(": ") + .styled((s) -> s.withColor(0xbbbbbb)), + Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) + ), 0.7f, + 7, + false); + + if (!children.isEmpty()) { + ClickableWidget name_widget = children.get(0); + name_widget.setPosition(parent.getX() + getX() + 5, parent.getY() + getY() + 5); + + ClickableWidget delete_button = children.get(1); + delete_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4); + + ClickableWidget export_button = children.get(2); + export_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 14); + + ClickableWidget replay_button = children.get(3); + replay_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 28); + } + + for (ClickableWidget child : children) { + child.render(ctx, mouseX, mouseY, delta); + } + } +} diff --git a/src/main/resources/assets/repeating-mod/lang/en_us.json b/src/main/resources/assets/repeating-mod/lang/en_us.json index cfab5c4..774b7d8 100644 --- a/src/main/resources/assets/repeating-mod/lang/en_us.json +++ b/src/main/resources/assets/repeating-mod/lang/en_us.json @@ -11,14 +11,23 @@ "text.repeating-mod.record_tooltip": "Start/stop recording all activities", "text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions", "text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay", - "text.repeating-mod.export": "Export record", + "text.repeating-mod.export_record": "Export record", "text.repeating-mod.import": "Import record", "text.repeating-mod.export_tooltip": "Exporting a recording to a file", - "text.repeating-mod.import_tooltip": "Importing an entry from the import.txt file", + "text.repeating-mod.import_tooltip": "Importing a recording from a file", "text.repeating-mod.dev": "In development...", "text.repeating-mod.nan_pos_delay": "No pos timer", "text.repeating-mod.pos_delay": "Pos timer: %s ticks", "text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", + "text.repeating-mod.unnamed": "Unnamed Record", + "text.repeating-mod.on_loop": "Enable repeat", + "text.repeating-mod.off_loop": "Disable repeat", + "text.repeating-mod.recorded_at": "Recorded at", + "text.repeating-mod.author": "Author", + "text.repeating-mod.delete": "Delete", + "text.repeating-mod.start": "Start", + "text.repeating-mod.stop": "Stop", + "text.repeating-mod.export": "Export", "message.repeating-mod.replay_start": "Replay started", "message.repeating-mod.replay_stop": "Replay finished", diff --git a/src/main/resources/assets/repeating-mod/lang/ru_ru.json b/src/main/resources/assets/repeating-mod/lang/ru_ru.json index 3fd30f4..2cf3521 100644 --- a/src/main/resources/assets/repeating-mod/lang/ru_ru.json +++ b/src/main/resources/assets/repeating-mod/lang/ru_ru.json @@ -10,19 +10,29 @@ "text.repeating-mod.stop_replay": "Остановить повтор", "text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий", "text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий", - "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор повтора записанных действий", - "text.repeating-mod.export": "Экспорт записи", + "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор записи", "text.repeating-mod.import": "Импорт записи", "text.repeating-mod.export_tooltip": "Экспорт записи в файл", - "text.repeating-mod.import_tooltip": "Импорт записи из файла import.txt", + "text.repeating-mod.import_tooltip": "Импорт записи из файла", "text.repeating-mod.dev": "В разработке...", "text.repeating-mod.nan_pos_delay": "Таймера позиции нету", "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", "text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", + "text.repeating-mod.unnamed": "Безымянная Запись", + "text.repeating-mod.on_loop": "Включить повторение", + "text.repeating-mod.off_loop": "Выключить повторение", + "text.repeating-mod.recorded_at": "Записано в", + "text.repeating-mod.author": "Автор", "message.repeating-mod.replay_start": "Повтор начат", "message.repeating-mod.replay_stop": "Повтор закончен", "message.repeating-mod.record_start": "Запись начата", - "message.repeating-mod.record_stop": "Запись закончена" + "message.repeating-mod.record_stop": "Запись закончена", + + "text.repeating-mod.export_record": "Экпорт записи", + "text.repeating-mod.export": "Экспорт", + "text.repeating-mod.delete": "Удалить", + "text.repeating-mod.start": "Старт", + "text.repeating-mod.stop": "Стоп" } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index cdf4d39..7a2119f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -19,7 +19,7 @@ "environment": "client", "entrypoints": { "client": [ - "themixray.repeating.mod.RepeatingMod" + "themixray.repeating.mod.Main" ] }, "mixins": [ From efcc61b5788389b32b643a91698aa175e44bfd10 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 13:28:45 +0300 Subject: [PATCH 13/72] browse file directory exception --- .../themixray/repeating/mod/widget/RecordWidget.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java index d8a2d21..21ce25f 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -104,9 +104,13 @@ public class RecordWidget implements Drawable, Widget { if (Desktop.isDesktopSupported()) { Desktop desk = Desktop.getDesktop(); try { - desk.browse(record.getFile().toURI()); - } catch (IOException e) { - throw new RuntimeException(e); + desk.browseFileDirectory(record.getFile()); + } catch (Exception e) { + try { + desk.browse(record.getFile().toURI()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } } } }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 14, 38, 13).build(); From ae03aa9627bbdc16780256f1469b0e0eb6ee7a39 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 14:09:06 +0300 Subject: [PATCH 14/72] menu interface move --- .../themixray/repeating/mod/RecordList.java | 2 +- .../themixray/repeating/mod/RecordState.java | 5 --- .../repeating/mod/RepeatingScreen.java | 38 ++++++++++--------- .../mod/widget/RecordListWidget.java | 4 +- .../repeating/mod/widget/RecordWidget.java | 18 ++++----- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index b1a3c1d..e17a0e8 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -18,7 +18,7 @@ public class RecordList { public RecordList(File folder) { this.folder = folder; this.records = new ArrayList<>(); - this.widget = new RecordListWidget(0, 0, 120, 80); + this.widget = new RecordListWidget(0, 0, 180, 200); } public List getRecords() { diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index fe844c8..239194a 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -128,11 +128,6 @@ public class RecordState { } Files.write(file.toPath(), text.toString().getBytes()); - - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - desktop.browse(file.toURI()); - } } public static RecordState load(File file) throws Exception { diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index ed3ccb8..46d5877 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -11,6 +11,7 @@ import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; +import themixray.repeating.mod.widget.RecordListWidget; import java.awt.*; import java.io.File; @@ -69,16 +70,23 @@ public class RepeatingScreen extends Screen { @Override protected void init() { + RecordListWidget list_widget = Main.me.record_list.getWidget(); + + list_widget.setX(width / 2 + 2); + list_widget.setY(height / 2 - list_widget.getHeight() / 2); + list_widget.init(this); + + record_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.start_record"), button -> { - if (!Main.me.is_replaying) { - if (Main.me.is_recording) - Main.me.stopRecording(); - else Main.me.startRecording(); - updateButtons(); - } - }) - .dimensions(width / 2 - 60, height / 2 - 76, 120, 20) + Text.translatable("text.repeating-mod.start_record"), button -> { + if (!Main.me.is_replaying) { + if (Main.me.is_recording) + Main.me.stopRecording(); + else Main.me.startRecording(); + updateButtons(); + } + }) + .dimensions(width / 2 - 120, height / 2 - 32, 120, 20) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) .build(); @@ -86,14 +94,14 @@ public class RepeatingScreen extends Screen { Main.me.loop_replay = !Main.me.loop_replay; updateButtons(); }) - .dimensions(width / 2 - 60, height / 2 - 54, 120, 20) + .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) .build(); pos_delay_slider = new SliderWidget( - width / 2 - 60, height / 2 - 32, 120, 20, + width / 2 - 120, height / 2 + 12, 120, 20, (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), (Main.me.record_pos_delay+1d)/101d) { @Override @@ -153,14 +161,10 @@ public class RepeatingScreen extends Screen { } }}).start(); }) - .dimensions(width / 2 - 60, height / 2 - 10, 120, 20) + .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) .build(); - Main.me.record_list.getWidget().setX(width / 2 - Main.me.record_list.getWidget().getWidth() / 2); - Main.me.record_list.getWidget().setY(height / 2 + 12); - Main.me.record_list.getWidget().init(this); - was_build = true; updateButtons(); diff --git a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java index 3106ae5..5bcd5a4 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java @@ -51,7 +51,7 @@ public class RecordListWidget extends ScrollableWidget { } public void addWidget(RecordState record) { - RecordWidget widget = new RecordWidget(0, 0, width, 50, record, this); + RecordWidget widget = new RecordWidget(0, 0, width, 55, record, this); widget.init(null); widgets.add(0, widget); } @@ -72,7 +72,7 @@ public class RecordListWidget extends ScrollableWidget { @Override protected int getContentsHeight() { - return !widgets.isEmpty() ? widgets.size() * 50 + (widgets.size() - 1) * 2 : 0; + return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0; } public void init(RepeatingScreen screen) { diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java index 21ce25f..85900f7 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -77,7 +77,7 @@ public class RecordWidget implements Drawable, Widget { Main.client.textRenderer, parent.getX() + getX() + 5, parent.getY() + getY() + 5, - 70, + 102, 10, Text.empty()); @@ -96,7 +96,7 @@ public class RecordWidget implements Drawable, Widget { ButtonWidget delete_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.delete"), (i) -> { record.remove(); - }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4, 38, 13).build(); + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4, 65, 13).build(); children.add(delete_button); @@ -113,7 +113,7 @@ public class RecordWidget implements Drawable, Widget { } } } - }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 14, 38, 13).build(); + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build(); children.add(export_button); @@ -125,7 +125,7 @@ public class RecordWidget implements Drawable, Widget { i.setMessage(Text.translatable("text.repeating-mod.stop")); Main.me.now_record = record; Main.me.startReplay(); - }).dimensions(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 28, 38, 13) + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); children.add(replay_button); @@ -171,8 +171,8 @@ public class RecordWidget implements Drawable, Widget { .append(": ") .styled((s) -> s.withColor(0xbbbbbb)), Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) - ), 0.7f, - 7, + ), 1, + 9, false); if (!children.isEmpty()) { @@ -180,13 +180,13 @@ public class RecordWidget implements Drawable, Widget { name_widget.setPosition(parent.getX() + getX() + 5, parent.getY() + getY() + 5); ClickableWidget delete_button = children.get(1); - delete_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4); + delete_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4); ClickableWidget export_button = children.get(2); - export_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 14); + export_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14); ClickableWidget replay_button = children.get(3); - replay_button.setPosition(parent.getX() + getX() + 77,parent.getY() + getY() + 4 + 28); + replay_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28); } for (ClickableWidget child : children) { From 2f026586303f99916201c7a5fdfc1ca78e5ebc00 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 14:09:46 +0300 Subject: [PATCH 15/72] change version to 1.1.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a80352b..e62f209 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,6 @@ loader_version=0.15.10 fabric_version=0.92.1+1.20.1 # Mod Properties -mod_version = 1.0.6 +mod_version = 1.1.0 maven_group = themixray.repeating.mod archives_base_name = repeating-mod \ No newline at end of file From c5132221ae086277d2c109f6e142cde0947f2903 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 18:17:22 +0300 Subject: [PATCH 16/72] 1.20.4 compatibility --- build.gradle | 97 --------- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradlew | 244 ----------------------- gradlew.bat | 92 --------- settings.gradle | 10 - 6 files changed, 449 deletions(-) delete mode 100644 build.gradle delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat delete mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 220e433..0000000 --- a/build.gradle +++ /dev/null @@ -1,97 +0,0 @@ -plugins { - id 'fabric-loom' version '1.1-SNAPSHOT' - id 'maven-publish' -} - -version = project.mod_version -group = project.maven_group - -repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. - maven { url 'https://maven.wispforest.io' } - mavenCentral() -} - -dependencies { - compileOnly 'org.projectlombok:lombok:1.18.24' - annotationProcessor 'org.projectlombok:lombok:1.18.24' - - //add joml - modImplementation 'org.joml:joml:1.10.4' - include 'org.joml:joml:1.10.4' - - // To change the versions see the gradle.properties file - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - - // Fabric API. This is technically optional, but you probably want it anyway. - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - - // Uncomment the following line to enable the deprecated Fabric API modules. - // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. - - // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" - -// modImplementation "io.wispforest:owo-lib:${project.owo_version}" -// // only if you plan to use owo-config -// annotationProcessor "io.wispforest:owo-lib:${project.owo_version}" - - // include this if you don't want force your users to install owo - // sentinel will warn them and give the option to download it automatically -// include "io.wispforest:owo-sentinel:${project.owo_version}" -} - -base { - archivesName = project.archives_base_name -} - -processResources { - inputs.property "version", project.version - - filesMatching("fabric.mod.json") { - expand "version": project.version - } -} - -tasks.withType(JavaCompile).configureEach { - // Minecraft 1.18 (1.18-pre2) upwards uses Java 17. - it.options.release = 17 -} - -java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -jar { - from("LICENSE") { - rename { "${it}_${base.archivesName.get()}"} - } -} - -// configure the maven publication -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ccebba7710deaf9f98673a68957ea02138b60d0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61608 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index fc10b60..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 79a61d4..0000000 --- a/gradlew +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 6689b85..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index b02216b..0000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -pluginManagement { - repositories { - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - mavenCentral() - gradlePluginPortal() - } -} From 27627e8404d5f3881bc0e46e34ddfb7c218bb693 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 18:17:31 +0300 Subject: [PATCH 17/72] 1.20.4 compatibility --- build.gradle | 83 +++++ gradle.properties | 8 +- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++ gradlew.bat | 92 +++++ .../themixray/repeating/mod/EasyConfig.java | 103 ++++++ remappedSrc/themixray/repeating/mod/Main.java | 325 ++++++++++++++++++ .../themixray/repeating/mod/RecordList.java | 77 +++++ .../themixray/repeating/mod/RecordState.java | 169 +++++++++ .../repeating/mod/RenderListener.java | 11 + .../repeating/mod/RepeatingScreen.java | 193 +++++++++++ .../themixray/repeating/mod/TickTask.java | 94 +++++ .../mod/event/RecordBlockBreakEvent.java | 32 ++ .../mod/event/RecordBlockInteractEvent.java | 46 +++ .../repeating/mod/event/RecordDelayEvent.java | 29 ++ .../repeating/mod/event/RecordEvent.java | 28 ++ .../repeating/mod/event/RecordInputEvent.java | 147 ++++++++ .../repeating/mod/event/RecordMoveEvent.java | 42 +++ .../repeating/mod/mixin/ClientMixin.java | 24 ++ .../repeating/mod/mixin/EntityMixin.java | 31 ++ .../repeating/mod/mixin/InputMixin.java | 20 ++ .../repeating/mod/mixin/MovementMixin.java | 44 +++ .../repeating/mod/mixin/NetworkMixin.java | 29 ++ .../repeating/mod/mixin/RendererMixin.java | 21 ++ .../repeating/mod/render/RenderHelper.java | 200 +++++++++++ .../repeating/mod/render/RenderSystem.java | 13 + .../mod/render/buffer/BufferManager.java | 48 +++ .../repeating/mod/render/buffer/Vertex.java | 30 ++ .../mod/render/buffer/WorldBuffer.java | 82 +++++ .../repeating/mod/render/shader/Shader.java | 42 +++ .../mod/render/shader/ShaderManager.java | 97 ++++++ .../mod/widget/RecordListWidget.java | 150 ++++++++ .../repeating/mod/widget/RecordWidget.java | 196 +++++++++++ settings.gradle | 10 + .../repeating/mod/RepeatingScreen.java | 8 +- .../repeating/mod/mixin/EntityMixin.java | 15 +- .../repeating/mod/mixin/NetworkMixin.java | 26 +- 38 files changed, 2790 insertions(+), 31 deletions(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 remappedSrc/themixray/repeating/mod/EasyConfig.java create mode 100644 remappedSrc/themixray/repeating/mod/Main.java create mode 100644 remappedSrc/themixray/repeating/mod/RecordList.java create mode 100644 remappedSrc/themixray/repeating/mod/RecordState.java create mode 100644 remappedSrc/themixray/repeating/mod/RenderListener.java create mode 100644 remappedSrc/themixray/repeating/mod/RepeatingScreen.java create mode 100644 remappedSrc/themixray/repeating/mod/TickTask.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/InputMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java create mode 100644 remappedSrc/themixray/repeating/mod/render/RenderHelper.java create mode 100644 remappedSrc/themixray/repeating/mod/render/RenderSystem.java create mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java create mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java create mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java create mode 100644 remappedSrc/themixray/repeating/mod/render/shader/Shader.java create mode 100644 remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java create mode 100644 remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java create mode 100644 remappedSrc/themixray/repeating/mod/widget/RecordWidget.java create mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..84a69d1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,83 @@ +plugins { + id 'fabric-loom' version '1.6-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name +} + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + + //add joml + modImplementation 'org.joml:joml:1.10.4' + include 'org.joml:joml:1.10.4' + + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 17 +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +jar { + from("LICENSE") { + rename { "${it}_${project.base.archivesName.get()}"} + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava", MavenPublication) { + artifactId = project.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index e62f209..8655bba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,14 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 +minecraft_version=1.20.4 +yarn_mappings=1.20.4+build.3 loader_version=0.15.10 #Fabric api -fabric_version=0.92.1+1.20.1 +fabric_version=0.97.0+1.20.4 # Mod Properties -mod_version = 1.1.0 +mod_version = 1.1.0+1.20.4 maven_group = themixray.repeating.mod archives_base_name = repeating-mod \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 Vo0ScuU6DbNL5P=uVd@+Z4*=P9Hgf;~ literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b82aa23 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/remappedSrc/themixray/repeating/mod/EasyConfig.java b/remappedSrc/themixray/repeating/mod/EasyConfig.java new file mode 100644 index 0000000..8836722 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/EasyConfig.java @@ -0,0 +1,103 @@ +package themixray.repeating.mod; + +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(File f, Map def) { + this.path = f.toPath(); + this.file = f; + this.data = new HashMap<>(); + + if (!file.exists()) { + try { + file.createNewFile(); + write(def); + } catch (IOException e) { + e.printStackTrace(); + } + } + + reload(); + + for (Map.Entry m:def.entrySet()) + if (!data.containsKey(m.getKey())) + data.put(m.getKey(),m.getValue()); + + save(); + } + public EasyConfig(Path f, Map def) { + this(f.toFile(),def); + } + public EasyConfig(String parent,String child,Map def) { + this(new File(parent,child),def); + } + public EasyConfig(File parent,String child,Map def) { + this(new File(parent,child),def); + } + public EasyConfig(Path parent,String child,Map def) { + this(new File(parent.toFile(),child),def); + } + + public EasyConfig(File f) { + this(f,new HashMap<>()); + } + public EasyConfig(Path path) { + this(path.toFile(),new HashMap<>()); + } + public EasyConfig(String parent,String child) { + this(new File(parent,child),new HashMap<>()); + } + public EasyConfig(File parent,String child) { + this(new File(parent,child),new HashMap<>()); + } + public EasyConfig(Path parent,String child) { + this(new File(parent.toFile(),child),new HashMap<>()); + } + + public void reload() { + data = read(); + } + public void save() { + write(data); + } + + private String toText(Map p) { + StringBuilder t = new StringBuilder(); + for (Map.Entry e:p.entrySet()) + t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); + return t.toString(); + } + private Map toMap(String j) { + Map m = new HashMap<>(); + for (String l:j.split("\n")) { + String s[] = l.split("="); + m.put(s[0],s[1]); + } + return m; + } + + 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, toText(p).getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/remappedSrc/themixray/repeating/mod/Main.java b/remappedSrc/themixray/repeating/mod/Main.java new file mode 100644 index 0000000..6a851ea --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/Main.java @@ -0,0 +1,325 @@ +package themixray.repeating.mod; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.Vec3d; +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import themixray.repeating.mod.event.RecordDelayEvent; +import themixray.repeating.mod.event.RecordEvent; +import themixray.repeating.mod.event.RecordInputEvent; +import themixray.repeating.mod.event.RecordMoveEvent; +import themixray.repeating.mod.render.RenderHelper; +import themixray.repeating.mod.render.RenderSystem; +import themixray.repeating.mod.render.buffer.WorldBuffer; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class Main implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); + public static final MinecraftClient client = MinecraftClient.getInstance(); + public static final FabricLoader loader = FabricLoader.getInstance(); + public static Main me; + + public RecordList record_list; + public RecordState now_record; + + public boolean is_recording = false; + public long last_record = -1; + public TickTask move_tick = null; + + public TickTask replay_tick = null; + public boolean is_replaying = false; + public boolean loop_replay = false; + public static RecordInputEvent input_replay = null; + + public long living_ticks = 0; + + public static RepeatingScreen menu; + private static KeyBinding menu_key; + private static KeyBinding toggle_replay_key; + private static KeyBinding toggle_record_key; + + public long record_pos_delay = 20; + + public static Random rand = new Random(); + + public EasyConfig conf; + public File records_folder; + + @Override + public void onInitializeClient() { + LOGGER.info("Repeating mod initialized"); + me = this; + + now_record = null; + + records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); + if (!records_folder.exists()) records_folder.mkdir(); + + record_list = new RecordList(records_folder); + record_list.loadRecords(); + + RenderSystem.init(); + WorldRenderEvents.LAST.register(context -> { + WorldBuffer buffer = RenderHelper.startTri(context); + if (now_record != null) { + Vec3d start_pos = now_record.getStartRecordPos(); + Vec3d finish_pos = now_record.getFinishRecordPos(); + + if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128)); + if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128)); + } + RenderHelper.endTri(buffer); + }); + + Map def = new HashMap<>(); + def.put("record_pos_delay", String.valueOf(record_pos_delay)); + + conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); + + record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); + + menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.menu",InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_J,"text.repeating-mod.name")); + toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, + -1,"text.repeating-mod.name")); + toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, + -1,"text.repeating-mod.name")); + + menu = new RepeatingScreen(); + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (menu_key.wasPressed()) + client.setScreen(menu); + if (toggle_replay_key.wasPressed()) { + if (now_record != null) { + if (!is_recording) { + if (is_replaying) + stopReplay(); + else startReplay(); + menu.updateButtons(); + } + } + } + if (toggle_record_key.wasPressed()) { + if (!is_replaying) { + if (is_recording) + stopRecording(); + else startRecording(); + menu.updateButtons(); + } + } + }); + + new TickTask(0,0) { + @Override + public void run() { + living_ticks++; + } + }; + + System.setProperty("java.awt.headless", "false"); + } + + public void setNowRecord(RecordState record) { + now_record = record; + } + + public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { + RenderHelper.drawRectFromTri(buffer, + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + color); + } + + public void startRecording() { + is_recording = true; + menu.updateButtons(); + + now_record = record_list.newRecord(); + + Vec3d start_pos = client.player.getPos(); + now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); + now_record.setStartRecordPos(start_pos); + + if (record_pos_delay > 0) { + move_tick = new TickTask( + record_pos_delay, + record_pos_delay) { + @Override + public void run() { + now_record.addEvent(new RecordMoveEvent(client.player.getPos(), + client.player.getHeadYaw(), client.player.getPitch())); + } + }; + } + + sendMessage(Text.translatable("message.repeating-mod.record_start")); + } + + public void recordTick(RecordEvent e) { + if (is_recording) { + long now = living_ticks; + if (last_record != -1) { + long diff = now - last_record - 2; + if (diff > 0) now_record.addEvent(new RecordDelayEvent(diff)); + } + now_record.addEvent(e); + last_record = now; + } + } + + public void recordAllInput() { + if (client.player == null) { + stopRecording(); + return; + } + + RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input")); + if (l == null) { + RecordInputEvent e = new RecordInputEvent( + client.player.input.sneaking, + client.player.input.jumping, + client.player.input.movementSideways, + client.player.input.movementForward, + client.player.input.pressingForward, + client.player.input.pressingBack, + client.player.input.pressingLeft, + client.player.input.pressingRight, + client.player.getHeadYaw(), + client.player.getBodyYaw(), + client.player.getPitch(), + client.player.isSprinting(), + client.player.getYaw(), + client.player.getMovementSpeed()); + recordTick(e); + } else { + RecordInputEvent e = new RecordInputEvent( + ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, + ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, + (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, + (((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, + ((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, + ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, + ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, + ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, + client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), + ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), + client.player.getYaw(),client.player.getMovementSpeed()); + + if (!(e.isEmpty() && + e.yaw == l.yaw && + e.head_yaw == l.head_yaw && + e.pitch == l.pitch && + e.body_yaw == l.body_yaw)) { + e.fillEmpty(l); + recordTick(e); + } + } + } + + public void stopRecording() { + is_recording = false; + now_record.setFinishRecordPos(client.player.getPos()); + try { + now_record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (move_tick != null) { + move_tick.cancel(); + move_tick = null; + } + menu.updateButtons(); + last_record = -1; + sendMessage(Text.translatable("message.repeating-mod.record_stop")); + } + + + public void startReplay() { + is_recording = false; + is_replaying = true; + menu.updateButtons(); + + List events = now_record.getEvents(); + + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { + public int replay_index = 0; + + @Override + public void run() { + if (!is_replaying) cancel(); + RecordEvent e = events.get(replay_index); + if (e instanceof RecordDelayEvent) { + setDelay(((RecordDelayEvent) e).delay); + } else { + e.replay(); + } + + replay_index++; + if (!loop_replay) { + if (replay_index == events.size()) { + stopReplay(); + cancel(); + } + } else if (replay_index == events.size()) { + replay_index = 0; + } + } + }; + + sendMessage(Text.translatable("message.repeating-mod.replay_start")); + } + + public void stopReplay() { + is_recording = false; + is_replaying = false; + if (replay_tick != null) { + replay_tick.cancel(); + replay_tick = null; + } + menu.updateButtons(); + record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); + sendMessage(Text.translatable("message.repeating-mod.replay_stop")); + } + + public static void sendMessage(MutableText text) { + client.player.sendMessage(Text.literal("[") + .append(Text.translatable("text.repeating-mod.name")) + .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) + .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); + } + + public static void sendDebug(String s) { + client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); + } +} diff --git a/remappedSrc/themixray/repeating/mod/RecordList.java b/remappedSrc/themixray/repeating/mod/RecordList.java new file mode 100644 index 0000000..e17a0e8 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/RecordList.java @@ -0,0 +1,77 @@ +package themixray.repeating.mod; + +import net.minecraft.text.Text; +import themixray.repeating.mod.widget.RecordListWidget; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class RecordList { + private final File folder; + private List records; + private RecordListWidget widget; + + public RecordList(File folder) { + this.folder = folder; + this.records = new ArrayList<>(); + this.widget = new RecordListWidget(0, 0, 180, 200); + } + + public List getRecords() { + return records; + } + + public File getFolder() { + return folder; + } + + public RecordListWidget getWidget() { + return widget; + } + + public void loadRecords() { + for (File file : folder.listFiles()) { + try { + addRecord(file); + } catch (Exception e) {} + } + } + + public void addRecord(File file) throws Exception { + addRecord(RecordState.load(file)); + } + + public void addRecord(RecordState record) { + records.add(record); + widget.addWidget(record); + } + + public void removeRecord(RecordState record) { + records.remove(record); + widget.removeWidget(record); + } + + public RecordState newRecord() { + Date date = new Date(); + String name = "Unnamed"; + String author = Main.client.player.getName().getString(); + + File file = new File(Main.me.records_folder, + "record_" + RecordState.FILE_DATE_FORMAT.format(date) + + "_" + Main.rand.nextInt(10) + ".rrm"); + + RecordState state = new RecordState( + file, name, date, author, + new ArrayList<>(), + null, + null); + + addRecord(state); + + return state; + } +} diff --git a/remappedSrc/themixray/repeating/mod/RecordState.java b/remappedSrc/themixray/repeating/mod/RecordState.java new file mode 100644 index 0000000..239194a --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/RecordState.java @@ -0,0 +1,169 @@ +package themixray.repeating.mod; + +import com.google.common.collect.Lists; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.event.RecordEvent; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class RecordState { + public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM.dd.yyyy HH:mm:ss"); + public static SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy_HH-mm-ss"); + + private final File file; + private String name; + private Date date; + private String author; + + private List events; + private Vec3d start_record_pos; + private Vec3d finish_record_pos; + + public RecordState(File file, + String name, + Date date, + String author, + List events, + Vec3d start_record_pos, + Vec3d finish_record_pos) { + this.file = file; + this.name = name; + this.date = date; + this.author = author; + + this.events = events; + this.start_record_pos = start_record_pos; + this.finish_record_pos = finish_record_pos; + } + + public File getFile() { + return file; + } + + public String getName() { + return name; + } + + public String getAuthor() { + return author; + } + + public Date getDate() { + return date; + } + + public List getEvents() { + return events; + } + + public Vec3d getFinishRecordPos() { + return finish_record_pos; + } + + public Vec3d getStartRecordPos() { + return start_record_pos; + } + + public void setAuthor(String author) { + this.author = author; + } + + public void setDate(Date date) { + this.date = date; + } + + public void setName(String name) { + this.name = name; + } + + public void setEvents(List events) { + this.events = events; + } + + public void setFinishRecordPos(Vec3d finish_record_pos) { + this.finish_record_pos = finish_record_pos; + } + + public void setStartRecordPos(Vec3d start_record_pos) { + this.start_record_pos = start_record_pos; + } + + public void addEvent(RecordEvent event) { + events.add(event); + } + + public RecordEvent getLastEvent(String type) { + for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { + if (r.getType().equals(type)) { + return r; + } + } + return null; + } + + public void save() throws IOException { + StringBuilder text = new StringBuilder(); + + text.append(name).append("\n") + .append(DATE_FORMAT.format(date)).append("\n") + .append(author).append("\n"); + + text.append(start_record_pos.getX()).append("n") + .append(start_record_pos.getY()).append("n") + .append(start_record_pos.getZ()).append("x") + .append(finish_record_pos.getX()).append("n") + .append(finish_record_pos.getY()).append("n") + .append(finish_record_pos.getZ()); + + for (int i = 0; i < events.size(); i++) { + text.append("\n"); + text.append(events.get(i).serialize()); + } + + Files.write(file.toPath(), text.toString().getBytes()); + } + + public static RecordState load(File file) throws Exception { + String text = Files.readString(file.toPath()); + List lines = List.of(text.split("\n")); + + List signature = lines.subList(0,4); + + String name = signature.get(0); + Date date = DATE_FORMAT.parse(signature.get(1)); + String author = signature.get(2); + + String record_pos = signature.get(3); + + String[] lss0 = record_pos.split("x"); + String[] lss1 = lss0[0].split("n"); + String[] lss2 = lss0[1].split("n"); + + Vec3d start_record_pos = new Vec3d( + Float.parseFloat(lss1[0]), + Float.parseFloat(lss1[1]), + Float.parseFloat(lss1[2])); + Vec3d finish_record_pos = new Vec3d( + Float.parseFloat(lss2[0]), + Float.parseFloat(lss2[1]), + Float.parseFloat(lss2[2])); + + List event_lines = lines.subList(4,lines.size()); + List events = event_lines.stream().map(RecordEvent::deserialize).toList(); + + return new RecordState(file, name, date, author, events, start_record_pos, finish_record_pos); + } + + public void remove() { + file.delete(); + Main.me.record_list.removeRecord(this); + Main.me.record_list.getWidget().removeWidget(this); + } +} diff --git a/remappedSrc/themixray/repeating/mod/RenderListener.java b/remappedSrc/themixray/repeating/mod/RenderListener.java new file mode 100644 index 0000000..9992e48 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/RenderListener.java @@ -0,0 +1,11 @@ +package themixray.repeating.mod; + +import net.minecraft.client.gui.DrawContext; + +public interface RenderListener { + default boolean beforeRender() { + return true; + } + + void render(DrawContext context, int mouseX, int mouseY, float delta); +} diff --git a/remappedSrc/themixray/repeating/mod/RepeatingScreen.java b/remappedSrc/themixray/repeating/mod/RepeatingScreen.java new file mode 100644 index 0000000..19609e4 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/RepeatingScreen.java @@ -0,0 +1,193 @@ +package themixray.repeating.mod; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.SliderWidget; +import net.minecraft.text.Text; +import themixray.repeating.mod.widget.RecordListWidget; + +import java.awt.*; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +@Environment(EnvType.CLIENT) +public class RepeatingScreen extends Screen { + private static List render_listeners = new ArrayList<>(); + + public ButtonWidget record_btn; + public ButtonWidget loop_btn; + public ButtonWidget import_btn; + + public SliderWidget pos_delay_slider; + + public boolean was_build = false; + + protected RepeatingScreen() { + super(Text.empty()); + } + + public static void addRenderListener(RenderListener render) { + render_listeners.add(render); + } + + public static void removeRenderListener(RenderListener render) { + render_listeners.remove(render); + } + + public void updateButtons() { + if (was_build) { + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); + loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); + } + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context,mouseX,mouseY,delta); + + for (RenderListener l : render_listeners) { + if (l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } + + super.render(context, mouseX, mouseY, delta); + + for (RenderListener l : render_listeners) { + if (!l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } + } + + @Override + protected void init() { + RecordListWidget list_widget = Main.me.record_list.getWidget(); + + list_widget.method_46421(width / 2 + 2); + list_widget.method_46419(height / 2 - list_widget.getHeight() / 2); + list_widget.init(this); + + + record_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.start_record"), button -> { + if (!Main.me.is_replaying) { + if (Main.me.is_recording) + Main.me.stopRecording(); + else Main.me.startRecording(); + updateButtons(); + } + }) + .dimensions(width / 2 - 120, height / 2 - 32, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) + .build(); + + loop_btn = ButtonWidget.builder(Text.empty(), button -> { + Main.me.loop_replay = !Main.me.loop_replay; + updateButtons(); + }) + .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) + .build(); + + pos_delay_slider = new SliderWidget( + width / 2 - 120, height / 2 + 12, 120, 20, + (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), + (Main.me.record_pos_delay+1d)/101d) { + + @Override + protected void updateMessage() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); + } + + @Override + protected void applyValue() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); + Main.me.record_pos_delay = (long) v; + Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); + Main.me.conf.save(); + } + + @Override + public void onRelease(double mouseX, double mouseY) { + super.onRelease(mouseX, mouseY); + applyValue(); + } + + @Override + protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { + super.onDrag(mouseX, mouseY, deltaX, deltaY); + applyValue(); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + updateMessage(); + } + }; + pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); + + import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { + new Thread(() -> { + FileDialog fd = new FileDialog((java.awt.Frame) null); + fd.setMultipleMode(true); + fd.setName("Choose record files"); + fd.setTitle("Choose record files"); + fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); + fd.setVisible(true); + + File[] files = fd.getFiles(); + if (files != null) { + for (File file : files) { + try { + Main.me.record_list.addRecord(file); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }}).start(); + }) + .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) + .build(); + + was_build = true; + + updateButtons(); + + addDrawableChild(loop_btn); + addDrawableChild(record_btn); + addDrawableChild(import_btn); + addDrawableChild(pos_delay_slider); + } + + public T addDrawableChild(T drawableElement) { + return super.addDrawableChild(drawableElement); + } + + public T addDrawable(T drawable) { + return super.addDrawable(drawable); + } + + public T addSelectableChild(T child) { + return super.addSelectableChild(child); + } + + public void remove(Element child) { + super.remove(child); + } +} diff --git a/remappedSrc/themixray/repeating/mod/TickTask.java b/remappedSrc/themixray/repeating/mod/TickTask.java new file mode 100644 index 0000000..f3beed1 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/TickTask.java @@ -0,0 +1,94 @@ +package themixray.repeating.mod; + +import java.util.ArrayList; +import java.util.List; + +public abstract class TickTask implements Runnable { + public static List tasks = new ArrayList<>(); + + public static void tickTasks(TickAt at) { + for (TickTask t:new ArrayList<>(tasks)) + if (t.getAt() == at) t.tick(); + } + + private long living; + private long delay; + + private boolean is_repeating; + private long period; + + private boolean is_cancelled; + private TickAt at; + + public enum TickAt { + CLIENT_HEAD, CLIENT_TAIL, + MOVEMENT_HEAD, MOVEMENT_TAIL, + RENDER_HEAD, RENDER_TAIL + } + + public TickTask(long delay, TickAt at) { + this.is_cancelled = false; + this.is_repeating = false; + this.delay = delay; + this.living = 0; + this.period = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay, long period, TickAt at) { + this.is_cancelled = false; + this.is_repeating = true; + this.delay = delay; + this.period = period; + this.living = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay) { + this(delay,TickAt.CLIENT_HEAD); + } + + public TickTask(long delay, long period) { + this(delay,period,TickAt.CLIENT_HEAD); + } + + public void cancel() { + if (!is_cancelled) { + is_cancelled = true; + tasks.remove(this); + } + } + + public boolean isCancelled() { + return is_cancelled; + } + + public TickAt getAt() { + return at; + } + + public void setDelay(long delay) { + if (is_repeating) { + this.delay = delay; + } + } + public long getDelay() { + return this.delay; + } + + public void tick() { + if (living >= delay) { + if (is_repeating) { + delay = period; + run(); + living = -1; + } else { + run(); + cancel(); + } + } + living++; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java new file mode 100644 index 0000000..a39380c --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java @@ -0,0 +1,32 @@ +package themixray.repeating.mod.event; + +import net.minecraft.util.math.BlockPos; +import themixray.repeating.mod.Main; + +public class RecordBlockBreakEvent extends RecordEvent { + public BlockPos pos; + + public static RecordBlockBreakEvent fromArgs(String[] a) { + return new RecordBlockBreakEvent(new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2]))); + } + + public RecordBlockBreakEvent( + BlockPos pos) { + this.pos = pos; + } + + public void replay() { + Main.client.interactionManager.breakBlock(pos); + } + + public String serialize() { + return "b=" + pos.getX() + "&" + pos.getY() + "&" + pos.getZ(); + } + + public String getType() { + return "block_break"; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java new file mode 100644 index 0000000..59d0b76 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java @@ -0,0 +1,46 @@ +package themixray.repeating.mod.event; + +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.Main; + +public class RecordBlockInteractEvent extends RecordEvent { + public Hand hand; + public BlockHitResult hitResult; + + public static RecordBlockInteractEvent fromArgs(String[] a) { + return new RecordBlockInteractEvent( + Hand.valueOf(a[5]), + new BlockHitResult(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Direction.byId(Integer.parseInt(a[4])), + new BlockPos( + Integer.parseInt(a[0]), + Integer.parseInt(a[1]), + Integer.parseInt(a[2])), + a[3].equals("1"))); + } + + public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { + this.hand = hand; + this.hitResult = hitResult; + } + + public void replay() { + Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); + } + + public String serialize() { + return "i=" + hitResult.getBlockPos().getX() + "&" + hitResult.getBlockPos().getY() + "&" + hitResult.getBlockPos().getZ() + + "&" + (hitResult.isInsideBlock() ? "1" : "0") + "&" + hitResult.getSide().getId() + "&" + hand.name(); + } + + public String getType() { + return "block_interact"; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java new file mode 100644 index 0000000..7e4bcca --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java @@ -0,0 +1,29 @@ +package themixray.repeating.mod.event; + +public class RecordDelayEvent extends RecordEvent { + public long delay; + + public static RecordDelayEvent fromArgs(String[] a) { + return new RecordDelayEvent(Long.parseLong(a[0])); + } + + public RecordDelayEvent(long delay) { + this.delay = delay; + } + + public void replay() { + try { + Thread.sleep(delay / 20 * 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public String serialize() { + return "d=" + delay; + } + + public String getType() { + return "delay"; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordEvent.java new file mode 100644 index 0000000..e7c2ffe --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordEvent.java @@ -0,0 +1,28 @@ +package themixray.repeating.mod.event; + +public abstract class RecordEvent { + public abstract void replay(); + public abstract String serialize(); + public abstract String getType(); + + public static RecordEvent deserialize(String t) { + try { + String type = String.valueOf(t.charAt(0)); + String[] args = t.substring(2).split("&"); + if (type.equals("d")) { + return RecordDelayEvent.fromArgs(args); + } else if (type.equals("m")) { + return RecordMoveEvent.fromArgs(args); + } else if (type.equals("p")) { + return RecordInputEvent.fromArgs(args); + } else if (type.equals("b")) { + return RecordBlockBreakEvent.fromArgs(args); + } else if (type.equals("i")) { + return RecordBlockInteractEvent.fromArgs(args); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java new file mode 100644 index 0000000..a760440 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java @@ -0,0 +1,147 @@ +package themixray.repeating.mod.event; + +import themixray.repeating.mod.Main; + +public class RecordInputEvent extends RecordEvent { + public Boolean sneaking; + public Boolean jumping; + public Boolean pressingForward; + public Boolean pressingBack; + public Boolean pressingLeft; + public Boolean pressingRight; + public Boolean sprinting; + + public Float movementSideways; + public Float movementForward; + + public float yaw; + public float head_yaw; + public float body_yaw; + public float pitch; + public float speed; + + public static RecordInputEvent fromArgs(String[] a) { + return new RecordInputEvent( + (a[0].equals("n") ? null : a[0].equals("1")), + (a[1].equals("n") ? null : a[1].equals("1")), + (a[2].equals("n") ? null : Float.parseFloat(a[2])), + (a[3].equals("n") ? null : Float.parseFloat(a[3])), + (a[4].equals("n") ? null : a[4].equals("1")), + (a[5].equals("n") ? null : a[5].equals("1")), + (a[6].equals("n") ? null : a[6].equals("1")), + (a[7].equals("n") ? null : a[7].equals("1")), + Float.parseFloat(a[8]), Float.parseFloat(a[9]), + Float.parseFloat(a[10]), + (a[11].equals("n") ? null : a[11].equals("1")), + Float.parseFloat(a[12]), + Float.parseFloat(a[13])); + } + + public RecordInputEvent(Boolean sneaking, + Boolean jumping, + Float movementSideways, + Float movementForward, + Boolean pressingForward, + Boolean pressingBack, + Boolean pressingLeft, + Boolean pressingRight, + float head_yaw, + float body_yaw, + float head_pitch, + Boolean sprinting, + float yaw, + float speed) { + this.sneaking = sneaking; + this.jumping = jumping; + this.movementSideways = movementSideways; + this.movementForward = movementForward; + this.pressingForward = pressingForward; + this.pressingBack = pressingBack; + this.pressingLeft = pressingLeft; + this.pressingRight = pressingRight; + this.head_yaw = head_yaw; + this.body_yaw = body_yaw; + this.pitch = head_pitch; + this.sprinting = sprinting; + this.yaw = yaw; + this.speed = speed; + } + + public void fillEmpty(RecordInputEvent e) { + if (sneaking == null) sneaking = e.sneaking; + if (jumping == null) jumping = e.jumping; + if (movementSideways == null) movementSideways = e.movementSideways; + if (movementForward == null) movementForward = e.movementForward; + if (pressingForward == null) pressingForward = e.pressingForward; + if (pressingBack == null) pressingBack = e.pressingBack; + if (pressingLeft == null) pressingLeft = e.pressingLeft; + if (pressingRight == null) pressingRight = e.pressingRight; + if (sprinting == null) sprinting = e.sprinting; + } + + public boolean isEmpty() { + return sneaking == null && + jumping == null && + movementSideways == null && + movementForward == null && + pressingForward == null && + pressingBack == null && + pressingLeft == null && + pressingRight == null && + sprinting == null; + } + + public void replay() { + Main.input_replay = this; + } + + public void inputCallback() { + if (sprinting != null && Main.client.player.isSprinting() != sprinting) + Main.client.player.setSprinting(sprinting); + if (Main.client.player.getYaw() != yaw) + Main.client.player.setYaw(yaw); + if (Main.client.player.getHeadYaw() != head_yaw) + Main.client.player.setHeadYaw(head_yaw); + if (Main.client.player.getBodyYaw() != body_yaw) + Main.client.player.setBodyYaw(body_yaw); + if (Main.client.player.getPitch() != pitch) + Main.client.player.setPitch(pitch); + if (Main.client.player.getMovementSpeed() != speed) + Main.client.player.setMovementSpeed(speed); + if (sneaking != null && Main.client.player.input.sneaking != sneaking) + Main.client.player.input.sneaking = sneaking; + if (jumping != null && Main.client.player.input.jumping != jumping) + Main.client.player.input.jumping = jumping; + if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) + Main.client.player.input.movementSideways = movementSideways; + if (movementForward != null && Main.client.player.input.movementForward != movementForward) + Main.client.player.input.movementForward = movementForward; + if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) + Main.client.player.input.pressingForward = pressingForward; + if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) + Main.client.player.input.pressingBack = pressingBack; + if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) + Main.client.player.input.pressingLeft = pressingLeft; + if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) + Main.client.player.input.pressingRight = pressingRight; + } + + public String serialize() { + return "p=" + + ((sneaking == null) ? "n" : (sneaking ? "1" : "0")) + "&" + + ((jumping == null) ? "n" : (jumping ? "1" : "0")) + "&" + + ((movementSideways == null) ? "n" : movementSideways) + "&" + + ((movementForward == null) ? "n" : movementForward) + "&" + + ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")) + "&" + + ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")) + "&" + + ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")) + "&" + + ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")) + "&" + + head_yaw + "&" + body_yaw + "&" + pitch + "&" + + ((sprinting == null) ? "n" : (sprinting ? "1" : "0") + + "&" + yaw + "&" + speed); + } + + public String getType() { + return "input"; + } +} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java new file mode 100644 index 0000000..7b964de --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java @@ -0,0 +1,42 @@ +package themixray.repeating.mod.event; + +import net.minecraft.entity.MovementType; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.Main; + +public class RecordMoveEvent extends RecordEvent { + public Vec3d vec; + public float yaw; + public float pitch; + + public static RecordMoveEvent fromArgs(String[] a) { + return new RecordMoveEvent(new Vec3d( + Double.parseDouble(a[0]), + Double.parseDouble(a[1]), + Double.parseDouble(a[2])), + Float.parseFloat(a[3]), + Float.parseFloat(a[4])); + } + + public RecordMoveEvent(Vec3d vec, float yaw, float pitch) { + this.vec = vec; + this.yaw = yaw; + this.pitch = pitch; + } + + public void replay() { + Vec3d p = Main.client.player.getPos(); + Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); + Main.client.player.move(MovementType.SELF, v); + Main.client.player.setYaw(yaw); + Main.client.player.setPitch(pitch); + } + + public String serialize() { + return "m=" + vec.getX() + "&" + vec.getY() + "&" + vec.getZ() + "&" + yaw + "&" + pitch; + } + + public String getType() { + return "move"; + } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java b/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java new file mode 100644 index 0000000..60e9e64 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java @@ -0,0 +1,24 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.MinecraftClient; +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.Main; +import themixray.repeating.mod.TickTask; + +@Mixin(MinecraftClient.class) +public abstract class ClientMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + if (Main.me.is_recording) + Main.me.recordAllInput(); + TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); + } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java b/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java new file mode 100644 index 0000000..eed7b58 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java @@ -0,0 +1,31 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import themixray.repeating.mod.Main; + +import java.util.UUID; + +@Mixin(Entity.class) +public abstract class EntityMixin { + @Shadow public abstract UUID getUuid(); + + @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) + private void onSprint(boolean sprinting,CallbackInfo ci) { + if (Main.client.player != null) { + if (getUuid().equals(Main.client.player.getUuid())) { + if (Main.me.is_replaying) { + if (Main.input_replay != null && + Main.input_replay.sprinting != null && + Main.input_replay.sprinting != sprinting) { + ci.cancel(); + } + } + } + } + } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java b/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java new file mode 100644 index 0000000..800e147 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java @@ -0,0 +1,20 @@ +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.Main; + +@Mixin(KeyboardInput.class) +public abstract class InputMixin { + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { + if (Main.me.is_replaying) { + if (Main.input_replay != null) { + Main.input_replay.inputCallback(); + } + } + } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java b/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java new file mode 100644 index 0000000..45190ba --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java @@ -0,0 +1,44 @@ +package themixray.repeating.mod.mixin; + +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.HitResult; +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.Main; +import themixray.repeating.mod.event.RecordBlockBreakEvent; +import themixray.repeating.mod.event.RecordBlockInteractEvent; +import themixray.repeating.mod.TickTask; + +@Mixin(ClientPlayerEntity.class) +public abstract class MovementMixin { + + @Inject(at = @At(value = "HEAD"), method = "init") + private void init(CallbackInfo ci) { + PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { + if (Main.me.is_recording) + Main.me.recordTick(new RecordBlockBreakEvent(pos)); + }); + + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { + if (hitResult.getType().equals(HitResult.Type.BLOCK)) + if (Main.me.is_recording) + Main.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); + return ActionResult.PASS; + }); + } + + @Inject(at = @At(value = "HEAD"), method = "tickMovement") + private void onMoveHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onMoveTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); + } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java b/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java new file mode 100644 index 0000000..4f85878 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java @@ -0,0 +1,29 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.listener.ServerPlayPacketListener; +import net.minecraft.network.packet.Packet; +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 java.time.Duration; +import java.util.function.BooleanSupplier; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class NetworkMixin { +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") +// private void onSendPacket1Head(Packet packet, +// CallbackInfo ci) { +// +// } +// +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") +// private void onSendPacket2Head(Packet packet, +// BooleanSupplier sendCondition, +// Duration expirationTime, +// CallbackInfo ci) { +// +// } +} diff --git a/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java b/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java new file mode 100644 index 0000000..edb363d --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java @@ -0,0 +1,21 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.render.*; +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.TickTask; + +@Mixin(GameRenderer.class) +public abstract class RendererMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/RenderHelper.java b/remappedSrc/themixray/repeating/mod/render/RenderHelper.java new file mode 100644 index 0000000..4f80464 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/RenderHelper.java @@ -0,0 +1,200 @@ +package themixray.repeating.mod.render; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.util.math.Vec3d; +import themixray.repeating.mod.render.buffer.WorldBuffer; +import themixray.repeating.mod.render.shader.ShaderManager; + +import java.awt.*; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class RenderHelper { + public WorldBuffer startLines(WorldRenderContext context) { + glEnable(GL_LINE_SMOOTH); + return new WorldBuffer(GL_LINES, ShaderManager.getPositionColorShader(), context); + } + + public void endLines(WorldBuffer buffer) { + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + buffer.draw(); + glDepthMask(true); + glDisable(GL_BLEND); + } + + public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) { + buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + } + + public static WorldBuffer startTri(WorldRenderContext context) { + return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context); + } + + public static void endTri(WorldBuffer buffer) { + //glDepthRange(0, 0.7); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDepthMask(false); + buffer.draw(); + glDepthMask(true); + glEnable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthRange(0, 1); + } + + public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) { + buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + } + + public static void drawRectFromTri(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + Color color) { + drawTri(buffer, + x1, y1, z1, + x2, y2, z2, + x3, y3, z3, + color); + drawTri(buffer, + x3, y3, z3, + x4, y4, z4, + x1, y1, z1, + color); + } + + public void drawRectFromLines(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4, + Color color) { + drawLine(buffer, + x1, y1, z1, + x2, y2, z2, + color); + drawLine(buffer, + x2, y2, z2, + x3, y3, z3, + color); + drawLine(buffer, + x3, y3, z3, + x4, y4, z4, + color); + drawLine(buffer, + x4, y4, z4, + x1, y1, z1, + color); + } + + public void drawBoxFromTri(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + Color color) { + float[][] v = new float[][]{ + new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, + new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[0][0], v[0][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[0][2], + v[0][0], v[0][1], v[1][2], + v[0][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[0][1], v[1][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[0][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromTri(buffer, + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[1][0], v[1][1], v[0][2], + color); + } + + public void drawBoxFromLines(WorldBuffer buffer, + float x1, float y1, float z1, + float x2, float y2, float z2, + Color color) { + float[][] v = new float[][]{ + new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, + new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[0][0], v[0][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[0][2], + v[0][0], v[0][1], v[1][2], + v[0][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[0][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[0][1], v[1][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[0][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[0][2], + v[1][0], v[1][1], v[1][2], + v[0][0], v[1][1], v[1][2], + color); + + drawRectFromLines(buffer, + v[1][0], v[0][1], v[0][2], + v[1][0], v[0][1], v[1][2], + v[1][0], v[1][1], v[1][2], + v[1][0], v[1][1], v[0][2], + color); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/RenderSystem.java b/remappedSrc/themixray/repeating/mod/render/RenderSystem.java new file mode 100644 index 0000000..a5fa15c --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/RenderSystem.java @@ -0,0 +1,13 @@ +package themixray.repeating.mod.render; + +import lombok.experimental.UtilityClass; +import themixray.repeating.mod.render.buffer.BufferManager; +import themixray.repeating.mod.render.shader.ShaderManager; + +@UtilityClass +public class RenderSystem { + public static void init() { + BufferManager.init(); + ShaderManager.init(); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java b/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java new file mode 100644 index 0000000..76d2853 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java @@ -0,0 +1,48 @@ +package themixray.repeating.mod.render.buffer; + +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; + +import java.nio.FloatBuffer; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class BufferManager { + private int vao; + private int vbo; + + private int prevVao; + + public void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(client -> { + vao = glGenVertexArrays(); + vbo = glGenBuffers(); + }); + } + + public static void bindBuffer() { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + } + + public static void unbindBuffer() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + public static void writeBuffer(FloatBuffer buffer) { + glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); + } + + public static void draw(int drawMode, int verts) { + glDrawArrays(drawMode, 0, verts); + } + + public static void bind() { + prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING); + glBindVertexArray(vao); + } + + public static void unbind() { + glBindVertexArray(prevVao); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java b/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java new file mode 100644 index 0000000..5aaa113 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java @@ -0,0 +1,30 @@ +package themixray.repeating.mod.render.buffer; + +import lombok.Getter; + +public class Vertex { + @Getter + private float x; + @Getter + private float y; + @Getter + private float z; + @Getter + private float r; + @Getter + private float g; + @Getter + private float b; + @Getter + private float a; + + public Vertex(float x, float y, float z, float r, float g, float b, float a) { + this.x = x; + this.y = y; + this.z = z; + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} \ No newline at end of file diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java b/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java new file mode 100644 index 0000000..390f97d --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java @@ -0,0 +1,82 @@ +package themixray.repeating.mod.render.buffer; + +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.util.math.Vec3d; +import org.apache.commons.lang3.ArrayUtils; +import org.joml.Matrix4f; +import org.lwjgl.BufferUtils; +import themixray.repeating.mod.render.shader.Shader; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.opengl.GL33.*; + +public class WorldBuffer { + private final List vertices = new ArrayList<>(); + private final int drawMode; + private final Shader shader; + private FloatBuffer projectionMatrix; + private final Vec3d cameraPos; + + public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) { + this.drawMode = drawMode; + this.shader = shader; + this.cameraPos = worldRenderContext.camera().getPos(); + makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix()); + } + + public void vert(float x, float y, float z, float r, float g, float b, float a) { + vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a)); + } + + public void draw() { + BufferManager.bind(); + BufferManager.bindBuffer(); + + BufferManager.writeBuffer(getBuffer()); + applyProjectionMatrix(); + + glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, vertices.size() * 3 * 4L); + glEnableVertexAttribArray(1); + + BufferManager.unbindBuffer(); + + shader.bind(); + BufferManager.draw(drawMode, this.vertices.size()); + shader.unbind(); + + BufferManager.unbind(); + } + + private FloatBuffer getBuffer() { + FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7); + ArrayList floats = new ArrayList<>(); + for (Vertex vertex : vertices) { + floats.add(vertex.getX()); + floats.add(vertex.getY()); + floats.add(vertex.getZ()); + } + for (Vertex vertex : vertices) { + floats.add(vertex.getR()); + floats.add(vertex.getG()); + floats.add(vertex.getB()); + floats.add(vertex.getA()); + } + Float[] floatArray = new Float[floats.size()]; + floats.toArray(floatArray); + floatBuffer.put(ArrayUtils.toPrimitive(floatArray)); + return floatBuffer.flip(); + } + + private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) { + this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16)); + } + + private void applyProjectionMatrix() { + shader.uniformMatrix4f("u_projection", projectionMatrix); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/shader/Shader.java b/remappedSrc/themixray/repeating/mod/render/shader/Shader.java new file mode 100644 index 0000000..e11dd9a --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/shader/Shader.java @@ -0,0 +1,42 @@ +package themixray.repeating.mod.render.shader; + +import lombok.Getter; + +import java.nio.FloatBuffer; + +import static org.lwjgl.opengl.GL33.*; + +public class Shader { + @Getter + private final int id; + + + public Shader(String name) { + int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX); + int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT); + this.id = glCreateProgram(); + glAttachShader(id, v); + glAttachShader(id, f); + glLinkProgram(id); + } + + public void bind() { + glUseProgram(id); + } + + public void unbind() { + glUseProgram(0); + } + + public void uniformMatrix4f(String name, FloatBuffer matrix) { + bind(); + glUniformMatrix4fv(glGetUniformLocation(id, name), false, matrix); + unbind(); + } + + public void uniformValue2f(String name, float value1, float value2) { + bind(); + glUniform2f(glGetUniformLocation(id, name), value1, value2); + unbind(); + } +} diff --git a/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java b/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java new file mode 100644 index 0000000..f11c98b --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java @@ -0,0 +1,97 @@ +package themixray.repeating.mod.render.shader; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.TextureUtil; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.GlImportProcessor; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceFactory; +import net.minecraft.util.Identifier; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import static org.lwjgl.opengl.GL33.*; + +@UtilityClass +public class ShaderManager { + @Getter + private Shader positionColorShader; + + public void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders()); + } + + private void loadShaders() { + positionColorShader = new Shader("position_color"); + } + + public int loadShaderProgram(String name, ShaderType type) { + try { + boolean file_present = true; + ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager(); + Optional resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension)); + int i = glCreateShader(type.glType); + if (resource.isPresent()) { + GlStateManager.glShaderSource(i, new GlImportProcessor() { + @SneakyThrows + @Nullable + @Override + public String loadImport(boolean inline, String name) { + return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8); + } + }.readSource(readResourceAsString(resource.get().getInputStream()))); + } else file_present = false; + glCompileShader(i); + if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) { + String shaderInfo = StringUtils.trim(glGetShaderInfoLog(i, 32768)); + throw new IOException("Couldn't compile " + type.name + " program (" + name + ") : " + shaderInfo); + } + return i; + } catch (IOException e) { + e.printStackTrace(); + } + return 0; + } + + private String readResourceAsString(InputStream inputStream) { + ByteBuffer byteBuffer = null; + try { + byteBuffer = TextureUtil.readResource(inputStream); + int i = byteBuffer.position(); + byteBuffer.rewind(); + return MemoryUtil.memASCII(byteBuffer, i); + } catch (IOException ignored) { + } finally { + if (byteBuffer != null) { + MemoryUtil.memFree(byteBuffer); + } + } + return null; + } + + public enum ShaderType { + VERTEX("vertex", ".vsh", GL_VERTEX_SHADER), + FRAGMENT("fragment", ".fsh", GL_FRAGMENT_SHADER); + private final String name; + private final String fileExtension; + private final int glType; + + ShaderType(String name, String fileExtension, int glType) { + this.name = name; + this.fileExtension = fileExtension; + this.glType = glType; + } + } +} diff --git a/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java b/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java new file mode 100644 index 0000000..1ac42b2 --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java @@ -0,0 +1,150 @@ +package themixray.repeating.mod.widget; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.*; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import themixray.repeating.mod.Main; +import themixray.repeating.mod.RecordState; +import themixray.repeating.mod.RepeatingScreen; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class RecordListWidget extends ScrollableWidget { + private List widgets = new ArrayList<>(); + private boolean focused = false; + + public RecordListWidget(int x, int y, int width, int height) { + super(x,y,width,height,Text.empty()); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + focused = true; + boolean res = super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + focused = false; + return res; + } + + @Override + protected double getDeltaYPerScroll() { + return 10; + } + + @Override + protected void renderContents(DrawContext ctx, int mouseX, int mouseY, float delta) { + int y = 0; + for (RecordWidget wid: widgets) { + wid.method_46419(y); + wid.render(ctx, mouseX, (int) (mouseY + this.getScrollY()), delta); + + y += wid.getHeight(); + y += 2; + } + } + + public void addWidget(RecordState record) { + RecordWidget widget = new RecordWidget(0, 0, width, 55, record, this); + widget.init(null); + widgets.add(0, widget); + } + + public void removeWidget(RecordState record) { + widgets.removeIf(i -> i.getRecord().equals(record)); + } + + @Override + public void setFocused(boolean focused) { + + } + + @Override + public boolean isFocused() { + return focused; + } + + @Override + protected int getContentsHeight() { + return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0; + } + + public void init(RepeatingScreen screen) { + for (RecordWidget widget : widgets) { + widget.init(screen); + } + + screen.addDrawableChild(this); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + + } + + public RecordWidget getWidget(RecordState record) { + for (RecordWidget widget : widgets) { + if (widget.getRecord().equals(record)) { + return widget; + } + } + return null; + } + + public interface transport { + boolean check(ClickableWidget ch); + } + + public boolean checkTransport(transport tr) { + for (RecordWidget wid : widgets) { + for (ClickableWidget child : wid.getChildren()) { + if (tr.check(child)) { + return true; + } + } + } + return false; + } + + public boolean checkTransportNF(transport tr) { + for (RecordWidget wid : widgets) { + for (ClickableWidget child : wid.getChildren()) { + boolean res = tr.check(child); + + if (res) { + child.setFocused(true); + return true; + } else { + child.setFocused(false); + } + } + } + return false; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + return checkTransportNF((c) -> c.mouseClicked(mouseX, mouseY + this.getScrollY(), button)) || super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + return checkTransport((c) -> c.charTyped(chr, modifiers)) || super.charTyped(chr, modifiers); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + return checkTransport((c) -> c.keyPressed(keyCode, scanCode, modifiers)) || super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + return checkTransport((c) -> c.keyReleased(keyCode, scanCode, modifiers)) || super.keyReleased(keyCode, scanCode, modifiers); + } +} diff --git a/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java b/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java new file mode 100644 index 0000000..bef439a --- /dev/null +++ b/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java @@ -0,0 +1,196 @@ +package themixray.repeating.mod.widget; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.*; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import themixray.repeating.mod.Main; +import themixray.repeating.mod.RecordState; +import themixray.repeating.mod.RenderListener; +import themixray.repeating.mod.RepeatingScreen; + +import java.awt.*; +import java.io.IOException; +import java.util.ArrayList; + +import java.util.List; +import java.util.function.Consumer; + +public class RecordWidget implements Drawable, Widget { + private RecordState record; + + private List children; + + private RecordListWidget parent; + + private int x; + private int y; + private int width; + private int height; + + public RecordWidget(int x, int y, int width, int height, RecordState record, RecordListWidget parent) { + this.parent = parent; + this.record = record; + + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.children = new ArrayList<>(); + } + + public void method_46421(int x) { + this.x = x; + } + public void method_46419(int y) { + this.y = y; + } + public int getX() { + return x; + } + public int getY() { + return y; + } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + + public List getChildren() { + return children; + } + + @Override + public void forEachChild(Consumer consumer) { + children.forEach(consumer); + } + + public void init(RepeatingScreen screen) { + this.children = new ArrayList<>(); + + TextFieldWidget name_widget = new TextFieldWidget( + Main.client.textRenderer, + parent.getX() + getX() + 5, + parent.getY() + getY() + 5, + 102, + 10, + Text.empty()); + + name_widget.setText(record.getName()); + + name_widget.setChangedListener((s) -> { + record.setName(s); + try { + record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + children.add(name_widget); + + ButtonWidget delete_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.delete"), (i) -> { + record.remove(); + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4, 65, 13).build(); + + children.add(delete_button); + + ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> { + if (Desktop.isDesktopSupported()) { + Desktop desk = Desktop.getDesktop(); + try { + desk.browseFileDirectory(record.getFile()); + } catch (Exception e) { + try { + desk.browse(record.getFile().toURI()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + } + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build(); + + children.add(export_button); + + ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.start"), (i) -> { + if (Main.me.is_replaying) { + Main.me.stopReplay(); + } + + i.setMessage(Text.translatable("text.repeating-mod.stop")); + Main.me.now_record = record; + Main.me.startReplay(); + }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); + + children.add(replay_button); + } + + public RecordState getRecord() { + return record; + } + + public void drawText(int x, int y, DrawContext ctx, List lines, float size, int line_height, boolean shadow) { + ctx.getMatrices().push(); + ctx.getMatrices().scale(size, size, size); + + int now_y = y; + + for (Text line : lines) { + ctx.drawText(Main.client.textRenderer, line, (int) (x / size), (int) (now_y / size), line.getStyle().getColor().getRgb(), shadow); + now_y += line_height; + } + + ctx.getMatrices().pop(); + } + + @Override + public void render(DrawContext ctx, int mouseX, int mouseY, float delta) { + int color = record.equals(Main.me.now_record) ? 0xFF555555 : 0xFF333333; + + ctx.fill(parent.getX() + getX(), + parent.getY() + getY(), + parent.getX() + getX() + getWidth(), + parent.getY() + getY() + getHeight(), + color); + + drawText( + parent.getX() + getX() + 5, + parent.getY() + getY() + 5 + 12, + ctx, List.of( + Text.translatable("text.repeating-mod.recorded_at") + .append(": ") + .styled((s) -> s.withColor(0xbbbbbb)), + Text.literal(RecordState.DATE_FORMAT.format(record.getDate())).styled((s) -> s.withColor(0xffffff)), + Text.translatable("text.repeating-mod.author") + .append(": ") + .styled((s) -> s.withColor(0xbbbbbb)), + Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) + ), 1, + 9, + false); + + if (!children.isEmpty()) { + ClickableWidget name_widget = children.get(0); + name_widget.setPosition(parent.getX() + getX() + 5, parent.getY() + getY() + 5); + + ClickableWidget delete_button = children.get(1); + delete_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4); + + ClickableWidget export_button = children.get(2); + export_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14); + + ClickableWidget replay_button = children.get(3); + replay_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28); + } + + for (ClickableWidget child : children) { + child.render(ctx, mouseX, mouseY, delta); + } + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..75c4d72 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + mavenCentral() + gradlePluginPortal() + } +} \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 46d5877..7d13d12 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -51,7 +51,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context); + renderBackground(context,mouseX,mouseY,delta); for (RenderListener l : render_listeners) { if (l.beforeRender()) { @@ -132,12 +132,6 @@ public class RepeatingScreen extends Screen { super.onDrag(mouseX, mouseY, deltaX, deltaY); applyValue(); } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - updateMessage(); - } }; pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); diff --git a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java index 8d45d11..eed7b58 100644 --- a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java @@ -16,13 +16,14 @@ public abstract class EntityMixin { @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) private void onSprint(boolean sprinting,CallbackInfo ci) { - if (getUuid().equals(Main.client.player.getUuid())) { - if (Main.me.is_replaying) { - if (Main.input_replay != null && - Main.input_replay.sprinting != null && - Main.input_replay.sprinting != sprinting) { - ci.cancel(); - return; + if (Main.client.player != null) { + if (getUuid().equals(Main.client.player.getUuid())) { + if (Main.me.is_replaying) { + if (Main.input_replay != null && + Main.input_replay.sprinting != null && + Main.input_replay.sprinting != sprinting) { + ci.cancel(); + } } } } diff --git a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java index 1ffb3c6..4f85878 100644 --- a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java @@ -13,17 +13,17 @@ import java.util.function.BooleanSupplier; @Mixin(ClientPlayNetworkHandler.class) public abstract class NetworkMixin { - @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") - private void onSendPacket1Head(Packet packet, - CallbackInfo ci) { - - } - - @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") - private void onSendPacket2Head(Packet packet, - BooleanSupplier sendCondition, - Duration expirationTime, - CallbackInfo ci) { - - } +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") +// private void onSendPacket1Head(Packet packet, +// CallbackInfo ci) { +// +// } +// +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") +// private void onSendPacket2Head(Packet packet, +// BooleanSupplier sendCondition, +// Duration expirationTime, +// CallbackInfo ci) { +// +// } } From a1e4cb0c8d08c5863666cf133a045c8bae1413ca Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:43:20 +0300 Subject: [PATCH 18/72] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 157c82d..862db18 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,5 @@ Toggle replay | not specified ## Menu -![Repeating menu](https://github.com/MeexReay/repeating-mod/assets/127148610/da923fe5-d44d-421b-b601-2a65cb5543eb) +![Repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/5af93d22-09ce-4d1c-9f07-f663227da364) + From f603f5ad2794edbfad17018a10743be92eb9db25 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:50:46 +0300 Subject: [PATCH 19/72] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 862db18..7d3ebba 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ This mod can record your movements and play them back. ## Controls +Default Repeating Mod Buttons + ``` Menu | J Toggle recording | not specified @@ -16,3 +18,10 @@ Toggle replay | not specified ![Repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/5af93d22-09ce-4d1c-9f07-f663227da364) +## Todo + +- fix "Space in record name deletes record 0_0" +- fix "Save record on recording crashes the game" +- select record on click (you can see start and finish points) +- change icon in mod menu to new one +- practice mode like in geometry dash for parkours From 3f3ce10d7f460d29840a8cc1b52d9d21659ca0b5 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:52:44 +0300 Subject: [PATCH 20/72] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d3ebba..05760dd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This mod can record your movements and play them back. ## Controls -Default Repeating Mod Buttons +Default Repeating Mod keys ``` Menu | J @@ -16,12 +16,15 @@ Toggle replay | not specified ## Menu +How menu looks like + ![Repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/5af93d22-09ce-4d1c-9f07-f663227da364) ## Todo - fix "Space in record name deletes record 0_0" - fix "Save record on recording crashes the game" -- select record on click (you can see start and finish points) +- select record on click (you can see start and finish points when its selected) +- select record on import - change icon in mod menu to new one - practice mode like in geometry dash for parkours From e194d7f20478b6d839a4b2e8ec8f30d5105a1afa Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 18:55:50 +0300 Subject: [PATCH 21/72] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05760dd..7a41dc6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ How menu looks like ## Todo - fix "Space in record name deletes record 0_0" -- fix "Save record on recording crashes the game" +- fix "Save record (edit name) on recording crashes the game" - select record on click (you can see start and finish points when its selected) - select record on import - change icon in mod menu to new one From 70e97ecf92d5697f88f7d0b799d42f8186fa930e Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:38:08 +0300 Subject: [PATCH 22/72] Update README.md --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a41dc6..8673ff1 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,21 @@ How menu looks like ![Repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/5af93d22-09ce-4d1c-9f07-f663227da364) ## Todo +Releases dont have striked lines -- fix "Space in record name deletes record 0_0" -- fix "Save record (edit name) on recording crashes the game" +- ~~fix "Space in record name deletes record 0_0"~~ +- ~~fix "Save record (edit name) on recording crashes the game"~~ - select record on click (you can see start and finish points when its selected) - select record on import - change icon in mod menu to new one - practice mode like in geometry dash for parkours + +## How to build + +How to build mod from source + +``` +gradlew build +``` + +Compiled .jar file you can find in `build/libs` From 596ca3c0d344706a4d75d1a320317db55fae459e Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 20:06:18 +0300 Subject: [PATCH 23/72] some fixes --- gradle.properties | 8 ++++---- .../java/themixray/repeating/mod/RecordList.java | 6 ++++-- .../java/themixray/repeating/mod/RecordState.java | 9 ++++++++- .../themixray/repeating/mod/RepeatingScreen.java | 4 ++-- .../repeating/mod/widget/RecordListWidget.java | 14 ++++++++------ .../repeating/mod/widget/RecordWidget.java | 4 ++++ 6 files changed, 30 insertions(+), 15 deletions(-) diff --git a/gradle.properties b/gradle.properties index 8655bba..4fe0e23 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,14 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 +minecraft_version=1.20 +yarn_mappings=1.20+build.1 loader_version=0.15.10 #Fabric api -fabric_version=0.97.0+1.20.4 +fabric_version=0.83.0+1.20 # Mod Properties -mod_version = 1.1.0+1.20.4 +mod_version = 1.1.1+1.20 maven_group = themixray.repeating.mod archives_base_name = repeating-mod \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index e17a0e8..cb93741 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -41,8 +41,10 @@ public class RecordList { } } - public void addRecord(File file) throws Exception { - addRecord(RecordState.load(file)); + public RecordState addRecord(File file) throws Exception { + RecordState st = RecordState.load(file); + addRecord(st); + return st; } public void addRecord(RecordState record) { diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index 239194a..09f870d 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -109,6 +109,8 @@ public class RecordState { } public void save() throws IOException { + if (start_record_pos == null || finish_record_pos == null) return; + StringBuilder text = new StringBuilder(); text.append(name).append("\n") @@ -162,8 +164,13 @@ public class RecordState { } public void remove() { - file.delete(); Main.me.record_list.removeRecord(this); Main.me.record_list.getWidget().removeWidget(this); + if (Main.me.is_recording && this.equals(Main.me.now_record)) { + Main.me.stopRecording(); + Main.me.now_record = null; + return; + } + file.delete(); } } diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 7d13d12..b610d74 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -51,7 +51,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context,mouseX,mouseY,delta); + renderBackground(context); for (RenderListener l : render_listeners) { if (l.beforeRender()) { @@ -148,7 +148,7 @@ public class RepeatingScreen extends Screen { if (files != null) { for (File file : files) { try { - Main.me.record_list.addRecord(file); + Main.me.setNowRecord(Main.me.record_list.addRecord(file)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java index 5bcd5a4..ddded9f 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java @@ -104,7 +104,7 @@ public class RecordListWidget extends ScrollableWidget { public boolean checkTransport(transport tr) { for (RecordWidget wid : widgets) { for (ClickableWidget child : wid.getChildren()) { - if (tr.check(child)) { + if (child.isFocused() && tr.check(child)) { return true; } } @@ -112,12 +112,14 @@ public class RecordListWidget extends ScrollableWidget { return false; } - public boolean checkTransportNF(transport tr) { + public boolean checkTransportNF(double mouseX, double mouseY, int button) { for (RecordWidget wid : widgets) { - for (ClickableWidget child : wid.getChildren()) { - boolean res = tr.check(child); + if (wid.contains((int) mouseX, (int) mouseY)) { + Main.me.setNowRecord(wid.getRecord()); + } - if (res) { + for (ClickableWidget child : wid.getChildren()) { + if (child.mouseClicked(mouseX, mouseY, button)) { child.setFocused(true); return true; } else { @@ -130,7 +132,7 @@ public class RecordListWidget extends ScrollableWidget { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - return checkTransportNF((c) -> c.mouseClicked(mouseX, mouseY + this.getScrollY(), button)) || super.mouseClicked(mouseX, mouseY, button); + return checkTransportNF(mouseX, mouseY + getScrollY(), button) || super.mouseClicked(mouseX, mouseY, button); } @Override diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java index 85900f7..44d3e23 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -42,6 +42,10 @@ public class RecordWidget implements Drawable, Widget { this.children = new ArrayList<>(); } + public boolean contains(int x, int y) { + return parent.getX() + getX() <= x && parent.getY() + getY() <= y && x <= parent.getX() + getX() + getWidth() && y <= parent.getY() + getY() + getHeight(); + } + public void setX(int x) { this.x = x; } From 3e96a7b3608f704eb2336363d5103711a36a2409 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Mon, 22 Apr 2024 20:06:43 +0300 Subject: [PATCH 24/72] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8673ff1..c1ba7e5 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ Releases dont have striked lines - ~~fix "Space in record name deletes record 0_0"~~ - ~~fix "Save record (edit name) on recording crashes the game"~~ -- select record on click (you can see start and finish points when its selected) -- select record on import +- ~~select record on click (you can see start and finish points when its selected)~~ +- ~~select record on import~~ - change icon in mod menu to new one - practice mode like in geometry dash for parkours From b162304a0b55cc1894f3953ae32b74370a584c35 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Mon, 22 Apr 2024 20:08:14 +0300 Subject: [PATCH 25/72] change icon to new one --- src/main/resources/icon.png | Bin 1273 -> 1951 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png index f0987b0736c9a3e49f7d36094b8f186aece88780..0108e5d9525729c88be9864ff5b859f26e42a1a2 100644 GIT binary patch literal 1951 zcmeAS@N?(olHy`uVBq!ia0y~yV5nhWU})xGVqjo6u;Ya)0|NtNage(c!@6@aFBupZ zSkfJR9T^xl_H+M9WMyDr;4JWnEM{QfI}E~%$MaXDFfg!7dAc};RK&f#aj@)(hJZ`p z|Ns8y3ykDSm^eMwY2BQ(Q(I7S3d`Hj`rO;wa?gLCpZKv-eE&P~izl5;*feI^Wv&RA zkhVlnO7@ufQN?2~uFjZy@Z7<;G`?oTs&W&nh9bpPQzy!~Eat3QcXHP~@05;tDRmo8 zmKe=?Z7%0I%`JM0`kMOYpX|NMP6=O2{~|qAWlebe)nNO?xfA^Y_g?yQbn#0Kd!;+K zTh11F{%t93|H!cP^oLt7j`AE+7E^Dnp1mPbZ>7G=#8kyV_YS4ghYn@@*YH;tH-kn*#$nTKVYiCu??91wkSB3Up z0V`=b)Ux%ZaNs+`j4zw*GFw<%6{VJWZt;moPn==$v*zXu|H&H`7$oL)@U?7QwmkKa zl~eLj^R0%O-nJcX^Ui<0^Fi5GJqqNmi7l+HPARioPPcrOiOv4J<(B8l!xdet4lg*S z<8)ZYv+JbCp(ZCqBi~Cp;#a;&^jx(5$Jai$O~Xl9`R$j>cA9F+oZMX8mv*gr?sD4n zmF%&Jsfmw%+*9_wf3bMs1?i`*5gLh=0{g$5Z~wtt^=*o|+Tx{;tNTr^a$YQOs}vOT zTG77bSy<-pKDU z_>*pE_DW?9|C^wN3bx;NEt>K*z@)QO8=-_tmGkwzq&cQrH(fa_c=nib$h^f--0q1l zT3gkwY|`jlc1eA0sq&)O;*j_Q{E~~_U0q_D_H~QDOXYe0uR2~!bWA7NRr3E`ClH(W zsjOG%+A@EkR_`sm6E(eim-zo6G<+tY_*f;!V(K}q5zC`~)JI}>y52}nV}B-wKXfwd3{RljtwbA#%!W%`Z`mI>n5MO9D8Ds0_e&*k^T9U; z+wO`#H~72B)1|UGQmk#!#N!79Bo}e?&VD42wofM~zI&#kk&9`XGM#3Bw}=P48YwsY_8`QrAYz&WL3liHO{3Z^d1Q+NX1vO12vkX$7F@Nj>v)z$NN z#3w4wT2!^>PxptbZ5drz;cBKX$zON^-G1Hb|5H}8RdLp$Ux!jvLwEz-sveZTkX*F; z!D`-%B3H%ryfgNHu>QhxG4{h%@wjOhMlNkD4qGU{HIQ3S5YCyo)$NM>)9rnIb^neQ`Y}0y`$%9X}~JO+vgtr z`^KAhdmE@Q1e3Kw|5=OKthaC8v48r<;@dLYzvlhN54cAe(D0aXTkvnIy1ZgmK>G!;*{UA1=Q#<1TBw?Z3QZk}DN|bsB$u)bmB}$E)P&ekmP17q?bySKP(D@0Nfi zkI|*VeX@tPN-DKCD!$6IN!%cNWNYe@wi5!rzi$V|#JOd4_{{J4^w#8^zO{7T{Uf?* zzDBQd^dBlICptd-rnPRfu%BpL|8?m_yC0iuwG025v}gC>ANwrYFSlpK^_073+>vsZ zTwE@C&&uunI}6{IW1$tVx!&%6Y+{-w{^%(ChKKJgeBG)P<6@+rc9!MpKh!h6^m4v^ z@7vouJe*TH*fetAAAS4mcWU6}jB4q3SMTa%w;%kbcPG1cvy0@*y&vB0fB*WYj`I_P z8`J;A)l5^Y;^|B6sAJ!EQoxq&_$iNisrcj$xwgZWJav5c6G5y}iTJ+j=}XQZ{>i2h z+qzw95&Po>%v0=orl&6HKVEqJMJzopr0I!XmV*mgE literal 1273 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4h9AWhG(I6;tUK7Ea{HEjtmSN`?>!lvNA9* zC?tCX`7$t6sWC7#v@kII0;zw&z))(y!0;-8fx&791A}@qVib`3=FCzt`Q|Ei6yC4$wjG& zC8-QX21drZ2Bx}3mLZ0wRt5%ErbgNZ237_Jv1L;&Q8eV{r(~v8V$om-(I9)+T!Mjt zAu2V(GtJkRL5qQbfrEjSQHX&VXyCP-bzI>K$DM@3oV@09lD}x0 znVHO)Fd?*(BSM5fuxj73rkVeCNxfRG;jOdb^(>RjOG{4XPfVQW8QL(jI8L;&C{EH* z;6vq_{ts+%J9rakIE($-Kd&>uSN|ig{hg`B=>a+t*Dh{TjI4;b7N^Sebv9qggQ=mm zlFcoxD?4x4m7M$**cQtE`}P!1|7$TpwJN92n<$uYTem- zZBCA^bRoCJfdy+;{Zce+(^#h(xIKmK;G%gCZ9}h$FkBQDl~=1>ylH*PD}fpPAJ*v! zCKMmo6Dj+p8!|&Sqbj8hU^K_<@;+DH!r_3w|J%NgA}V*dWJ#_-_~8TuTPmD z#Qr2GtLL)m13kk>j4i7ruYb({Z!IWrm-*ugA;y-~F9knwG#H+{yu@0sgMC8T%swri z2NLS-Nha(LtiJ8fCV3lB8AklEO@P6OB?DBc}e6|d?d%sAVH8SUX zXLP^5`pSt7)65RkDZft?c4UZnIrA_h%Z<1Ga>6qi=2=ECFjVBfHe#@0PuusT?Pu|& z1Hz6h3{Q(LUzwtOZRVL9#_ey}g&bK9)R|f&8zkMmwXUT|Z63#&#vfB`i#gXs%n$q= ztH7~k64TkKldM+9T3^%5`{y;aOSa@#mvyDQxqQQ|*KaSROlRMaSoC(fw#X%&&+h|Q zs)RCL@cF{RFspMOL&fjh)od3Sq#T$t7>JL|idm&_~D Date: Mon, 22 Apr 2024 20:08:26 +0300 Subject: [PATCH 26/72] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1ba7e5..e7a5de7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Releases dont have striked lines - ~~fix "Save record (edit name) on recording crashes the game"~~ - ~~select record on click (you can see start and finish points when its selected)~~ - ~~select record on import~~ -- change icon in mod menu to new one +- ~~change icon in mod menu to new one~~ - practice mode like in geometry dash for parkours ## How to build From 99c057c69256733e0c4feb5af4e424904a952cd6 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 03:47:53 +0300 Subject: [PATCH 27/72] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e7a5de7..7ff0d04 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Releases dont have striked lines - ~~select record on click (you can see start and finish points when its selected)~~ - ~~select record on import~~ - ~~change icon in mod menu to new one~~ +- create new preview.gif - practice mode like in geometry dash for parkours ## How to build From 50927fd2f728970f5aa36fc0167035c63fa7d9b4 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 03:48:40 +0300 Subject: [PATCH 28/72] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ff0d04..0067957 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Toggle replay | not specified How menu looks like -![Repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/5af93d22-09ce-4d1c-9f07-f663227da364) +![repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/4123068f-b150-45ae-8ae3-fcaa0e6bb9f8) ## Todo Releases dont have striked lines From 9596fedb6ac3f16b0cb3e8b3efde2edea380715d Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 03:55:36 +0300 Subject: [PATCH 29/72] license --- LICENSE | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/LICENSE b/LICENSE index 8b1a9d8..695250f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1 @@ - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - -Copyright (C) 2004 Sam Hocevar - -Everyone is permitted to copy and distribute verbatim or modified -copies of this license document, and changing it is allowed as long -as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. +All rights reserved unless explicitly stated. \ No newline at end of file From 26496ca3f256742d11e1f1adb3b9f2f959fe9b4c Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 14:46:56 +0300 Subject: [PATCH 30/72] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0067957..f0824a0 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Releases dont have striked lines - ~~select record on click (you can see start and finish points when its selected)~~ - ~~select record on import~~ - ~~change icon in mod menu to new one~~ +- fix "Crashes the game when leaving world while record is replaying" - create new preview.gif - practice mode like in geometry dash for parkours From ffc4423b1fe7b3c4643a4c57b91bbd847859dd6d Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 15:07:37 +0300 Subject: [PATCH 31/72] license --- LICENSE | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 695250f..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,202 @@ -All rights reserved unless explicitly stated. \ No newline at end of file + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From aad9a07484c5ba92525eebd7b25f42ab3d82af7f Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 15:13:55 +0300 Subject: [PATCH 32/72] license --- LICENSE | 564 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 395 insertions(+), 169 deletions(-) diff --git a/LICENSE b/LICENSE index d645695..2d58298 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,428 @@ +Attribution-ShareAlike 4.0 International - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +======================================================================= - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. - 1. Definitions. +Using Creative Commons Public Licenses - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +======================================================================= - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +Creative Commons Attribution-ShareAlike 4.0 International Public +License - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +Section 1 -- Definitions. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +Section 2 -- Scope. - END OF TERMS AND CONDITIONS + a. License grant. - APPENDIX: How to apply the Apache License to your work. + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + a. reproduce and Share the Licensed Material, in whole or + in part; and - Copyright [yyyy] [name of copyright owner] + b. produce, reproduce, and Share Adapted Material. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. - http://www.apache.org/licenses/LICENSE-2.0 + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. From a255e4b804369110c29d5112dca43de00fa76ae4 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 15:15:36 +0300 Subject: [PATCH 33/72] license --- LICENSE | 54 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/LICENSE b/LICENSE index 2d58298..7cdbe0b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Attribution-ShareAlike 4.0 International +Attribution-NonCommercial-ShareAlike 4.0 International ======================================================================= @@ -54,18 +54,18 @@ exhaustive, and do not form part of our licenses. ======================================================================= -Creative Commons Attribution-ShareAlike 4.0 International Public -License +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. +Attribution-NonCommercial-ShareAlike 4.0 International Public License +("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. Section 1 -- Definitions. @@ -84,7 +84,7 @@ Section 1 -- Definitions. and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. - c. BY-SA Compatible License means a license listed at + c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. @@ -108,7 +108,7 @@ Section 1 -- Definitions. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. + Public License are Attribution, NonCommercial, and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public @@ -122,7 +122,15 @@ Section 1 -- Definitions. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. - k. Share means to provide material to the public by any means or + k. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material @@ -130,13 +138,13 @@ Section 1 -- Definitions. public may access the material from a place and at a time individually chosen by them. - l. Sui Generis Database Rights means rights other than copyright + m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. - m. You means the individual or entity exercising the Licensed Rights + n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. @@ -150,9 +158,10 @@ Section 2 -- Scope. exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or - in part; and + in part, for NonCommercial purposes only; and - b. produce, reproduce, and Share Adapted Material. + b. produce, reproduce, and Share Adapted Material for + NonCommercial purposes only. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public @@ -220,7 +229,9 @@ Section 2 -- Scope. Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. Section 3 -- License Conditions. @@ -265,7 +276,6 @@ following conditions. reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. @@ -277,7 +287,7 @@ following conditions. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or - later, or a BY-SA Compatible License. + later, or a BY-NC-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition @@ -297,7 +307,8 @@ apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; + portion of the contents of the database for NonCommercial purposes + only; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database @@ -404,7 +415,6 @@ Section 8 -- Interpretation. that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. - ======================================================================= Creative Commons is not a party to its public From 28b4265a55b8a5423f629972b9e591e45e9156a8 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 15:17:39 +0300 Subject: [PATCH 34/72] license --- LICENSE | 54 ++++++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/LICENSE b/LICENSE index 7cdbe0b..2d58298 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Attribution-NonCommercial-ShareAlike 4.0 International +Attribution-ShareAlike 4.0 International ======================================================================= @@ -54,18 +54,18 @@ exhaustive, and do not form part of our licenses. ======================================================================= -Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International -Public License +Creative Commons Attribution-ShareAlike 4.0 International Public +License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons -Attribution-NonCommercial-ShareAlike 4.0 International Public License -("Public License"). To the extent this Public License may be -interpreted as a contract, You are granted the Licensed Rights in -consideration of Your acceptance of these terms and conditions, and the -Licensor grants You such rights in consideration of benefits the -Licensor receives from making the Licensed Material available under -these terms and conditions. +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. Section 1 -- Definitions. @@ -84,7 +84,7 @@ Section 1 -- Definitions. and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. - c. BY-NC-SA Compatible License means a license listed at + c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. @@ -108,7 +108,7 @@ Section 1 -- Definitions. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this - Public License are Attribution, NonCommercial, and ShareAlike. + Public License are Attribution and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public @@ -122,15 +122,7 @@ Section 1 -- Definitions. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. - k. NonCommercial means not primarily intended for or directed towards - commercial advantage or monetary compensation. For purposes of - this Public License, the exchange of the Licensed Material for - other material subject to Copyright and Similar Rights by digital - file-sharing or similar means is NonCommercial provided there is - no payment of monetary compensation in connection with the - exchange. - - l. Share means to provide material to the public by any means or + k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material @@ -138,13 +130,13 @@ Section 1 -- Definitions. public may access the material from a place and at a time individually chosen by them. - m. Sui Generis Database Rights means rights other than copyright + l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. - n. You means the individual or entity exercising the Licensed Rights + m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. @@ -158,10 +150,9 @@ Section 2 -- Scope. exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or - in part, for NonCommercial purposes only; and + in part; and - b. produce, reproduce, and Share Adapted Material for - NonCommercial purposes only. + b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public @@ -229,9 +220,7 @@ Section 2 -- Scope. Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties, including when - the Licensed Material is used other than for NonCommercial - purposes. + reserves any right to collect such royalties. Section 3 -- License Conditions. @@ -276,6 +265,7 @@ following conditions. reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. + 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. @@ -287,7 +277,7 @@ following conditions. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or - later, or a BY-NC-SA Compatible License. + later, or a BY-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition @@ -307,8 +297,7 @@ apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database for NonCommercial purposes - only; + portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database @@ -415,6 +404,7 @@ Section 8 -- Interpretation. that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + ======================================================================= Creative Commons is not a party to its public From e76c3dfccf0273c0c3712f9c69c27f1e912ee25b Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 19:18:14 +0300 Subject: [PATCH 35/72] events refactor + mixin testing --- .../{RecordBlockBreakEvent.java => events/BlockBreakEvent.java} | 0 .../BlockInteractEvent.java} | 0 .../mod/event/{RecordDelayEvent.java => events/DelayEvent.java} | 0 .../mod/event/{RecordInputEvent.java => events/InputEvent.java} | 0 .../mod/event/{RecordMoveEvent.java => events/MoveEvent.java} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/themixray/repeating/mod/event/{RecordBlockBreakEvent.java => events/BlockBreakEvent.java} (100%) rename src/main/java/themixray/repeating/mod/event/{RecordBlockInteractEvent.java => events/BlockInteractEvent.java} (100%) rename src/main/java/themixray/repeating/mod/event/{RecordDelayEvent.java => events/DelayEvent.java} (100%) rename src/main/java/themixray/repeating/mod/event/{RecordInputEvent.java => events/InputEvent.java} (100%) rename src/main/java/themixray/repeating/mod/event/{RecordMoveEvent.java => events/MoveEvent.java} (100%) diff --git a/src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/event/RecordBlockBreakEvent.java rename to src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java diff --git a/src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/event/RecordBlockInteractEvent.java rename to src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java diff --git a/src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java b/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/event/RecordDelayEvent.java rename to src/main/java/themixray/repeating/mod/event/events/DelayEvent.java diff --git a/src/main/java/themixray/repeating/mod/event/RecordInputEvent.java b/src/main/java/themixray/repeating/mod/event/events/InputEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/event/RecordInputEvent.java rename to src/main/java/themixray/repeating/mod/event/events/InputEvent.java diff --git a/src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java b/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java similarity index 100% rename from src/main/java/themixray/repeating/mod/event/RecordMoveEvent.java rename to src/main/java/themixray/repeating/mod/event/events/MoveEvent.java From f38629a2947c4d39e6eb72135b7a9aaa0f30bc2a Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 19:18:22 +0300 Subject: [PATCH 36/72] events refactor + mixin testing --- .../java/themixray/repeating/mod/Main.java | 24 ++-- .../themixray/repeating/mod/RecordState.java | 2 +- .../repeating/mod/event/RecordEvent.java | 32 +++--- .../repeating/mod/event/RecordEventType.java | 71 ++++++++++++ .../mod/event/events/BlockBreakEvent.java | 31 ++--- .../mod/event/events/BlockInteractEvent.java | 27 +++-- .../mod/event/events/DelayEvent.java | 22 ++-- .../mod/event/events/InputEvent.java | 107 +++++++++--------- .../repeating/mod/event/events/MoveEvent.java | 24 ++-- .../repeating/mod/mixin/MovementMixin.java | 8 +- .../repeating/mod/mixin/PlayerMixin.java | 23 ++++ .../repeating/mod/mixin/ScreenMixin.java | 64 +++++++++++ src/main/resources/repeating-mod.mixins.json | 4 +- 13 files changed, 300 insertions(+), 139 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/event/RecordEventType.java create mode 100644 src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java create mode 100644 src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/themixray/repeating/mod/Main.java index 9eb4e53..45a10cf 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/themixray/repeating/mod/Main.java @@ -15,10 +15,10 @@ import net.minecraft.util.math.Vec3d; import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import themixray.repeating.mod.event.RecordDelayEvent; +import themixray.repeating.mod.event.events.DelayEvent; import themixray.repeating.mod.event.RecordEvent; -import themixray.repeating.mod.event.RecordInputEvent; -import themixray.repeating.mod.event.RecordMoveEvent; +import themixray.repeating.mod.event.events.InputEvent; +import themixray.repeating.mod.event.events.MoveEvent; import themixray.repeating.mod.render.RenderHelper; import themixray.repeating.mod.render.RenderSystem; import themixray.repeating.mod.render.buffer.WorldBuffer; @@ -45,7 +45,7 @@ public class Main implements ClientModInitializer { public TickTask replay_tick = null; public boolean is_replaying = false; public boolean loop_replay = false; - public static RecordInputEvent input_replay = null; + public static InputEvent input_replay = null; public long living_ticks = 0; @@ -169,7 +169,7 @@ public class Main implements ClientModInitializer { now_record = record_list.newRecord(); Vec3d start_pos = client.player.getPos(); - now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); + now_record.addEvent(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); now_record.setStartRecordPos(start_pos); if (record_pos_delay > 0) { @@ -178,7 +178,7 @@ public class Main implements ClientModInitializer { record_pos_delay) { @Override public void run() { - now_record.addEvent(new RecordMoveEvent(client.player.getPos(), + now_record.addEvent(new MoveEvent(client.player.getPos(), client.player.getHeadYaw(), client.player.getPitch())); } }; @@ -192,7 +192,7 @@ public class Main implements ClientModInitializer { long now = living_ticks; if (last_record != -1) { long diff = now - last_record - 2; - if (diff > 0) now_record.addEvent(new RecordDelayEvent(diff)); + if (diff > 0) now_record.addEvent(new DelayEvent(diff)); } now_record.addEvent(e); last_record = now; @@ -205,9 +205,9 @@ public class Main implements ClientModInitializer { return; } - RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input")); + InputEvent l = ((InputEvent) now_record.getLastEvent("input")); if (l == null) { - RecordInputEvent e = new RecordInputEvent( + InputEvent e = new InputEvent( client.player.input.sneaking, client.player.input.jumping, client.player.input.movementSideways, @@ -224,7 +224,7 @@ public class Main implements ClientModInitializer { client.player.getMovementSpeed()); recordTick(e); } else { - RecordInputEvent e = new RecordInputEvent( + InputEvent e = new InputEvent( ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, @@ -280,8 +280,8 @@ public class Main implements ClientModInitializer { public void run() { if (!is_replaying) cancel(); RecordEvent e = events.get(replay_index); - if (e instanceof RecordDelayEvent) { - setDelay(((RecordDelayEvent) e).delay); + if (e instanceof DelayEvent) { + setDelay(((DelayEvent) e).delay); } else { e.replay(); } diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index 09f870d..2272f03 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -101,7 +101,7 @@ public class RecordState { public RecordEvent getLastEvent(String type) { for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { - if (r.getType().equals(type)) { + if (r.getType().getName().equals(type)) { return r; } } diff --git a/src/main/java/themixray/repeating/mod/event/RecordEvent.java b/src/main/java/themixray/repeating/mod/event/RecordEvent.java index e7c2ffe..7b56dc7 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordEvent.java @@ -1,28 +1,22 @@ package themixray.repeating.mod.event; +import themixray.repeating.mod.event.events.*; + public abstract class RecordEvent { public abstract void replay(); - public abstract String serialize(); - public abstract String getType(); - - public static RecordEvent deserialize(String t) { - try { - String type = String.valueOf(t.charAt(0)); - String[] args = t.substring(2).split("&"); - if (type.equals("d")) { - return RecordDelayEvent.fromArgs(args); - } else if (type.equals("m")) { - return RecordMoveEvent.fromArgs(args); - } else if (type.equals("p")) { - return RecordInputEvent.fromArgs(args); - } else if (type.equals("b")) { - return RecordBlockBreakEvent.fromArgs(args); - } else if (type.equals("i")) { - return RecordBlockInteractEvent.fromArgs(args); + public RecordEventType getType() { + for (RecordEventType ev : RecordEventType.values()) { + if (ev.getEventClass().equals(this.getClass())) { + return ev; } - } catch (Exception e) { - e.printStackTrace(); } return null; } + protected abstract String[] serializeArgs(); + public String serialize() { + return getType().getChar() + "=" + String.join("&", serializeArgs()); + } + public static RecordEvent deserialize(String t) { + return RecordEventType.getByChar(t.charAt(0)).deserialize(t.substring(2).split("&")); + } } diff --git a/src/main/java/themixray/repeating/mod/event/RecordEventType.java b/src/main/java/themixray/repeating/mod/event/RecordEventType.java new file mode 100644 index 0000000..45208d7 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/RecordEventType.java @@ -0,0 +1,71 @@ +package themixray.repeating.mod.event; + +import themixray.repeating.mod.event.events.*; + +import java.lang.reflect.InvocationTargetException; +import java.util.function.Consumer; + +public enum RecordEventType { + BLOCK_BREAK('b',"block_break",BlockBreakEvent.class), + BLOCK_INTERACT('i',"block_interact",BlockInteractEvent.class), + DELAY('d',"delay",DelayEvent.class), + INPUT('p',"input",InputEvent.class), + MOVE('m',"move",MoveEvent.class), + GUI_KEY_PRESS('r',"key_press",BlockBreakEvent.class), + GUI_KEY_RELEASE('s',"key_release",BlockBreakEvent.class), + GUI_CHAR_TYPE('h',"char_type",BlockBreakEvent.class), + GUI_MOUSE_CLICK('c',"mouse_click",BlockBreakEvent.class), + GUI_MOUSE_RELEASE('l',"mouse_release",BlockBreakEvent.class), + GUI_MOUSE_DRAG('g',"mouse_drag",BlockBreakEvent.class), + GUI_MOUSE_SCROLL('o',"mouse_scroll",BlockBreakEvent.class), + GUI_CLOSE('e',"close",BlockBreakEvent.class); + + private Class ev; + private char ch; + private String name; + + RecordEventType(char ch, String name, Class ev) { + this.ev = ev; + this.ch = ch; + this.name = name; + } + + public Class getEventClass() { + return ev; + } + + public char getChar() { + return ch; + } + + public String getName() { + return name; + } + + public RecordEvent deserialize(String[] args) { + try { + return (RecordEvent) ev + .getMethod("deserialize", String[].class) + .invoke(null, (Object) args); + } catch (Throwable e) { + return null; + } + } + + public String serialize(RecordEvent event) { + return event.serialize(); + } + + public static RecordEventType getByChar(String type) { + return getByChar(type.charAt(0)); + } + + public static RecordEventType getByChar(char ch) { + for (RecordEventType t : values()) { + if (t.getChar() == ch) { + return t; + } + } + return null; + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java index a39380c..a240052 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java @@ -1,32 +1,33 @@ -package themixray.repeating.mod.event; +package themixray.repeating.mod.event.events; import net.minecraft.util.math.BlockPos; import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; -public class RecordBlockBreakEvent extends RecordEvent { +public class BlockBreakEvent extends RecordEvent { public BlockPos pos; - public static RecordBlockBreakEvent fromArgs(String[] a) { - return new RecordBlockBreakEvent(new BlockPos( + public BlockBreakEvent( + BlockPos pos) { + this.pos = pos; + } + + public static BlockBreakEvent deserialize(String[] a) { + return new BlockBreakEvent(new BlockPos( Integer.parseInt(a[0]), Integer.parseInt(a[1]), Integer.parseInt(a[2]))); } - public RecordBlockBreakEvent( - BlockPos pos) { - this.pos = pos; + protected String[] serializeArgs() { + return new String[]{ + String.valueOf(pos.getX()), + String.valueOf(pos.getY()), + String.valueOf(pos.getZ()) + }; } public void replay() { Main.client.interactionManager.breakBlock(pos); } - - public String serialize() { - return "b=" + pos.getX() + "&" + pos.getY() + "&" + pos.getZ(); - } - - public String getType() { - return "block_break"; - } } diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java index 59d0b76..4eb99d1 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.event; +package themixray.repeating.mod.event.events; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; @@ -6,13 +6,14 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; -public class RecordBlockInteractEvent extends RecordEvent { +public class BlockInteractEvent extends RecordEvent { public Hand hand; public BlockHitResult hitResult; - public static RecordBlockInteractEvent fromArgs(String[] a) { - return new RecordBlockInteractEvent( + public static BlockInteractEvent deserialize(String[] a) { + return new BlockInteractEvent( Hand.valueOf(a[5]), new BlockHitResult(new Vec3d( Double.parseDouble(a[0]), @@ -26,7 +27,7 @@ public class RecordBlockInteractEvent extends RecordEvent { a[3].equals("1"))); } - public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { + public BlockInteractEvent(Hand hand, BlockHitResult hitResult) { this.hand = hand; this.hitResult = hitResult; } @@ -35,12 +36,14 @@ public class RecordBlockInteractEvent extends RecordEvent { Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); } - public String serialize() { - return "i=" + hitResult.getBlockPos().getX() + "&" + hitResult.getBlockPos().getY() + "&" + hitResult.getBlockPos().getZ() + - "&" + (hitResult.isInsideBlock() ? "1" : "0") + "&" + hitResult.getSide().getId() + "&" + hand.name(); - } - - public String getType() { - return "block_interact"; + protected String[] serializeArgs() { + return new String[]{ + String.valueOf(hitResult.getBlockPos().getX()), + String.valueOf(hitResult.getBlockPos().getY()), + String.valueOf(hitResult.getBlockPos().getZ()), + (hitResult.isInsideBlock() ? "1" : "0"), + String.valueOf(hitResult.getSide().getId()), + hand.name() + }; } } diff --git a/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java b/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java index 7e4bcca..1978702 100644 --- a/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java @@ -1,13 +1,15 @@ -package themixray.repeating.mod.event; +package themixray.repeating.mod.event.events; -public class RecordDelayEvent extends RecordEvent { +import themixray.repeating.mod.event.RecordEvent; + +public class DelayEvent extends RecordEvent { public long delay; - public static RecordDelayEvent fromArgs(String[] a) { - return new RecordDelayEvent(Long.parseLong(a[0])); + public static DelayEvent deserialize(String[] a) { + return new DelayEvent(Long.parseLong(a[0])); } - public RecordDelayEvent(long delay) { + public DelayEvent(long delay) { this.delay = delay; } @@ -19,11 +21,9 @@ public class RecordDelayEvent extends RecordEvent { } } - public String serialize() { - return "d=" + delay; - } - - public String getType() { - return "delay"; + protected String[] serializeArgs() { + return new String[]{ + String.valueOf(delay) + }; } } diff --git a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java b/src/main/java/themixray/repeating/mod/event/events/InputEvent.java index a760440..19c8b66 100644 --- a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/InputEvent.java @@ -1,8 +1,9 @@ -package themixray.repeating.mod.event; +package themixray.repeating.mod.event.events; import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; -public class RecordInputEvent extends RecordEvent { +public class InputEvent extends RecordEvent { public Boolean sneaking; public Boolean jumping; public Boolean pressingForward; @@ -20,37 +21,20 @@ public class RecordInputEvent extends RecordEvent { public float pitch; public float speed; - public static RecordInputEvent fromArgs(String[] a) { - return new RecordInputEvent( - (a[0].equals("n") ? null : a[0].equals("1")), - (a[1].equals("n") ? null : a[1].equals("1")), - (a[2].equals("n") ? null : Float.parseFloat(a[2])), - (a[3].equals("n") ? null : Float.parseFloat(a[3])), - (a[4].equals("n") ? null : a[4].equals("1")), - (a[5].equals("n") ? null : a[5].equals("1")), - (a[6].equals("n") ? null : a[6].equals("1")), - (a[7].equals("n") ? null : a[7].equals("1")), - Float.parseFloat(a[8]), Float.parseFloat(a[9]), - Float.parseFloat(a[10]), - (a[11].equals("n") ? null : a[11].equals("1")), - Float.parseFloat(a[12]), - Float.parseFloat(a[13])); - } - - public RecordInputEvent(Boolean sneaking, - Boolean jumping, - Float movementSideways, - Float movementForward, - Boolean pressingForward, - Boolean pressingBack, - Boolean pressingLeft, - Boolean pressingRight, - float head_yaw, - float body_yaw, - float head_pitch, - Boolean sprinting, - float yaw, - float speed) { + public InputEvent(Boolean sneaking, + Boolean jumping, + Float movementSideways, + Float movementForward, + Boolean pressingForward, + Boolean pressingBack, + Boolean pressingLeft, + Boolean pressingRight, + float head_yaw, + float body_yaw, + float head_pitch, + Boolean sprinting, + float yaw, + float speed) { this.sneaking = sneaking; this.jumping = jumping; this.movementSideways = movementSideways; @@ -67,7 +51,43 @@ public class RecordInputEvent extends RecordEvent { this.speed = speed; } - public void fillEmpty(RecordInputEvent e) { + public static InputEvent deserialize(String[] a) { + return new InputEvent( + (a[0].equals("n") ? null : a[0].equals("1")), + (a[1].equals("n") ? null : a[1].equals("1")), + (a[2].equals("n") ? null : Float.parseFloat(a[2])), + (a[3].equals("n") ? null : Float.parseFloat(a[3])), + (a[4].equals("n") ? null : a[4].equals("1")), + (a[5].equals("n") ? null : a[5].equals("1")), + (a[6].equals("n") ? null : a[6].equals("1")), + (a[7].equals("n") ? null : a[7].equals("1")), + Float.parseFloat(a[8]), Float.parseFloat(a[9]), + Float.parseFloat(a[10]), + (a[11].equals("n") ? null : a[11].equals("1")), + Float.parseFloat(a[12]), + Float.parseFloat(a[13])); + } + + protected String[] serializeArgs() { + return new String[] { + ((sneaking == null) ? "n" : (sneaking ? "1" : "0")), // sneaking + ((jumping == null) ? "n" : (jumping ? "1" : "0")), // jumping + ((movementSideways == null) ? "n" : String.valueOf(movementSideways)), // movement sideways + ((movementForward == null) ? "n" : String.valueOf(movementForward)), // movement forward + ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")), // pressing forward + ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")), // pressing back + ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")), // pressing left + ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")), // pressing right + String.valueOf(head_yaw), // head yaw + String.valueOf(body_yaw), // body yaw + String.valueOf(pitch), // pitch + ((sprinting == null) ? "n" : (sprinting ? "1" : "0")), // sprinting + String.valueOf(yaw), // yaw + String.valueOf(speed) // speed + }; + } + + public void fillEmpty(InputEvent e) { if (sneaking == null) sneaking = e.sneaking; if (jumping == null) jumping = e.jumping; if (movementSideways == null) movementSideways = e.movementSideways; @@ -125,23 +145,4 @@ public class RecordInputEvent extends RecordEvent { if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) Main.client.player.input.pressingRight = pressingRight; } - - public String serialize() { - return "p=" + - ((sneaking == null) ? "n" : (sneaking ? "1" : "0")) + "&" + - ((jumping == null) ? "n" : (jumping ? "1" : "0")) + "&" + - ((movementSideways == null) ? "n" : movementSideways) + "&" + - ((movementForward == null) ? "n" : movementForward) + "&" + - ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")) + "&" + - ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")) + "&" + - ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")) + "&" + - ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")) + "&" + - head_yaw + "&" + body_yaw + "&" + pitch + "&" + - ((sprinting == null) ? "n" : (sprinting ? "1" : "0") + - "&" + yaw + "&" + speed); - } - - public String getType() { - return "input"; - } } diff --git a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java b/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java index 7b964de..9957dbf 100644 --- a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java @@ -1,16 +1,17 @@ -package themixray.repeating.mod.event; +package themixray.repeating.mod.event.events; import net.minecraft.entity.MovementType; import net.minecraft.util.math.Vec3d; import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; -public class RecordMoveEvent extends RecordEvent { +public class MoveEvent extends RecordEvent { public Vec3d vec; public float yaw; public float pitch; - public static RecordMoveEvent fromArgs(String[] a) { - return new RecordMoveEvent(new Vec3d( + public static MoveEvent deserialize(String[] a) { + return new MoveEvent(new Vec3d( Double.parseDouble(a[0]), Double.parseDouble(a[1]), Double.parseDouble(a[2])), @@ -18,7 +19,7 @@ public class RecordMoveEvent extends RecordEvent { Float.parseFloat(a[4])); } - public RecordMoveEvent(Vec3d vec, float yaw, float pitch) { + public MoveEvent(Vec3d vec, float yaw, float pitch) { this.vec = vec; this.yaw = yaw; this.pitch = pitch; @@ -32,11 +33,12 @@ public class RecordMoveEvent extends RecordEvent { Main.client.player.setPitch(pitch); } - public String serialize() { - return "m=" + vec.getX() + "&" + vec.getY() + "&" + vec.getZ() + "&" + yaw + "&" + pitch; - } - - public String getType() { - return "move"; + protected String[] serializeArgs() { + return new String[]{ + String.valueOf(vec.getX()), + String.valueOf(vec.getZ()), + String.valueOf(yaw), + String.valueOf(pitch) + }; } } diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index 45190ba..b850676 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -10,8 +10,8 @@ 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.Main; -import themixray.repeating.mod.event.RecordBlockBreakEvent; -import themixray.repeating.mod.event.RecordBlockInteractEvent; +import themixray.repeating.mod.event.events.BlockBreakEvent; +import themixray.repeating.mod.event.events.BlockInteractEvent; import themixray.repeating.mod.TickTask; @Mixin(ClientPlayerEntity.class) @@ -21,13 +21,13 @@ public abstract class MovementMixin { private void init(CallbackInfo ci) { PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { if (Main.me.is_recording) - Main.me.recordTick(new RecordBlockBreakEvent(pos)); + Main.me.recordTick(new BlockBreakEvent(pos)); }); UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { if (hitResult.getType().equals(HitResult.Type.BLOCK)) if (Main.me.is_recording) - Main.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); + Main.me.recordTick(new BlockInteractEvent(hand,hitResult)); return ActionResult.PASS; }); } diff --git a/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java b/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java new file mode 100644 index 0000000..566b520 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java @@ -0,0 +1,23 @@ +package themixray.repeating.mod.mixin; + +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.Entity; +import net.minecraft.network.ClientConnection; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import themixray.repeating.mod.Main; + +import java.util.UUID; + +@Mixin(ClientConnection.class) +public abstract class PlayerMixin { + @Inject(at = @At(value = "HEAD"), method = "disconnect") + private void disconnect(Text disconnectReason, CallbackInfo ci) { + System.out.println("on client close"); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java new file mode 100644 index 0000000..1dd91e8 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java @@ -0,0 +1,64 @@ +package themixray.repeating.mod.mixin; + +import net.minecraft.client.gui.AbstractParentElement; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.ParentElement; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.render.GameRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import themixray.repeating.mod.TickTask; + +@Mixin(Screen.class) +public abstract class ScreenMixin extends AbstractParentElement implements Drawable { + @Inject(at = @At(value = "HEAD"), method = "close") + private void close(CallbackInfo ci) { + System.out.println("on screen close"); + } + + @Inject(at = @At(value = "HEAD"), method = "keyPressed") + private void keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { + System.out.println("on screen keyPressed"); + } + + @Override + public boolean charTyped(char chr, int modifiers) { + System.out.println("on screen charTyped"); + return super.charTyped(chr, modifiers); + } + + @Override + public boolean keyReleased(int keyCode, int scanCode, int modifiers) { + System.out.println("on screen keyReleased"); + return super.keyReleased(keyCode, scanCode, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + System.out.println("on screen mouseClicked"); + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + System.out.println("on screen mouseDragged"); + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int button) { + System.out.println("on screen mouseReleased"); + return super.mouseReleased(mouseX, mouseY, button); + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double amount) { + System.out.println("on screen mouseScrolled"); + return super.mouseScrolled(mouseX, mouseY, amount); + } +} diff --git a/src/main/resources/repeating-mod.mixins.json b/src/main/resources/repeating-mod.mixins.json index c03d7f6..6156b8f 100644 --- a/src/main/resources/repeating-mod.mixins.json +++ b/src/main/resources/repeating-mod.mixins.json @@ -10,7 +10,9 @@ "InputMixin", "RendererMixin", "EntityMixin", - "ClientMixin" + "ClientMixin", + "ScreenMixin", + "PlayerMixin" ], "injectors": { "defaultRequire": 1 From 8a6e680ff9e8747eed8b6e2b650de154f3d804ae Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 19:20:14 +0300 Subject: [PATCH 37/72] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f0824a0..2e3ff1c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Releases dont have striked lines - ~~select record on import~~ - ~~change icon in mod menu to new one~~ - fix "Crashes the game when leaving world while record is replaying" +- record gui mouse clicks and key pressing - create new preview.gif - practice mode like in geometry dash for parkours From 5fa8fba00dfc951e69847a984af84dc4ea9b7ad0 Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 20:23:06 +0300 Subject: [PATCH 38/72] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2e3ff1c..4d88726 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Releases dont have striked lines - fix "Crashes the game when leaving world while record is replaying" - record gui mouse clicks and key pressing - create new preview.gif +- copy file to record list when import - practice mode like in geometry dash for parkours ## How to build From 8d47394acc7f8442ab81f1bb1228282387b4ceac Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 20:37:36 +0300 Subject: [PATCH 39/72] gui not working, but im tried --- .../java/themixray/repeating/mod/Main.java | 12 +++- .../themixray/repeating/mod/TickTask.java | 3 +- .../repeating/mod/event/RecordEventType.java | 22 ++++--- .../mod/event/events/BlockBreakEvent.java | 4 +- .../mod/event/events/BlockInteractEvent.java | 4 +- .../mod/event/events/GuiCharTypeEvent.java | 34 +++++++++++ .../mod/event/events/GuiCloseEvent.java | 22 +++++++ .../mod/event/events/GuiKeyPressEvent.java | 38 ++++++++++++ .../mod/event/events/GuiKeyReleaseEvent.java | 38 ++++++++++++ .../mod/event/events/GuiMouseClickEvent.java | 38 ++++++++++++ .../mod/event/events/GuiMouseDragEvent.java | 46 +++++++++++++++ .../mod/event/events/GuiMouseMoveEvent.java | 34 +++++++++++ .../event/events/GuiMouseReleaseEvent.java | 38 ++++++++++++ .../mod/event/events/GuiMouseScrollEvent.java | 38 ++++++++++++ .../mod/event/events/InputEvent.java | 58 ++++++++++--------- .../repeating/mod/event/events/MoveEvent.java | 12 ++-- .../repeating/mod/mixin/PlayerMixin.java | 4 +- .../repeating/mod/mixin/ScreenMixin.java | 42 +++++++++++--- 18 files changed, 428 insertions(+), 59 deletions(-) create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java create mode 100644 src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/themixray/repeating/mod/Main.java index 45a10cf..37e5e28 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/themixray/repeating/mod/Main.java @@ -87,6 +87,10 @@ public class Main implements ClientModInitializer { RenderHelper.endTri(buffer); }); + ClientTickEvents.END_CLIENT_TICK.register(client -> { + TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); + }); + Map def = new HashMap<>(); def.put("record_pos_delay", String.valueOf(record_pos_delay)); @@ -273,12 +277,16 @@ public class Main implements ClientModInitializer { List events = now_record.getEvents(); - replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { public int replay_index = 0; @Override public void run() { - if (!is_replaying) cancel(); + if (!is_replaying) { + cancel(); + return; + } + RecordEvent e = events.get(replay_index); if (e instanceof DelayEvent) { setDelay(((DelayEvent) e).delay); diff --git a/src/main/java/themixray/repeating/mod/TickTask.java b/src/main/java/themixray/repeating/mod/TickTask.java index f3beed1..e014511 100644 --- a/src/main/java/themixray/repeating/mod/TickTask.java +++ b/src/main/java/themixray/repeating/mod/TickTask.java @@ -23,7 +23,8 @@ public abstract class TickTask implements Runnable { public enum TickAt { CLIENT_HEAD, CLIENT_TAIL, MOVEMENT_HEAD, MOVEMENT_TAIL, - RENDER_HEAD, RENDER_TAIL + RENDER_HEAD, RENDER_TAIL, + CLIENT_EVENT } public TickTask(long delay, TickAt at) { diff --git a/src/main/java/themixray/repeating/mod/event/RecordEventType.java b/src/main/java/themixray/repeating/mod/event/RecordEventType.java index 45208d7..957159f 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEventType.java +++ b/src/main/java/themixray/repeating/mod/event/RecordEventType.java @@ -2,23 +2,21 @@ package themixray.repeating.mod.event; import themixray.repeating.mod.event.events.*; -import java.lang.reflect.InvocationTargetException; -import java.util.function.Consumer; - public enum RecordEventType { BLOCK_BREAK('b',"block_break",BlockBreakEvent.class), BLOCK_INTERACT('i',"block_interact",BlockInteractEvent.class), DELAY('d',"delay",DelayEvent.class), INPUT('p',"input",InputEvent.class), - MOVE('m',"move",MoveEvent.class), - GUI_KEY_PRESS('r',"key_press",BlockBreakEvent.class), - GUI_KEY_RELEASE('s',"key_release",BlockBreakEvent.class), - GUI_CHAR_TYPE('h',"char_type",BlockBreakEvent.class), - GUI_MOUSE_CLICK('c',"mouse_click",BlockBreakEvent.class), - GUI_MOUSE_RELEASE('l',"mouse_release",BlockBreakEvent.class), - GUI_MOUSE_DRAG('g',"mouse_drag",BlockBreakEvent.class), - GUI_MOUSE_SCROLL('o',"mouse_scroll",BlockBreakEvent.class), - GUI_CLOSE('e',"close",BlockBreakEvent.class); + MOVE('m',"move",MoveEvent.class); +// GUI_KEY_PRESS('r',"key_press", GuiKeyPressEvent.class), +// GUI_KEY_RELEASE('s',"key_release",GuiKeyReleaseEvent.class), +// GUI_CHAR_TYPE('h',"char_type",GuiCharTypeEvent.class), +// GUI_MOUSE_CLICK('c',"mouse_click",GuiMouseClickEvent.class), +// GUI_MOUSE_RELEASE('l',"mouse_release",GuiMouseReleaseEvent.class), +// GUI_MOUSE_DRAG('g',"mouse_drag",GuiMouseDragEvent.class), +// GUI_MOUSE_MOVE('v',"mouse_move",GuiMouseMoveEvent.class), +// GUI_MOUSE_SCROLL('o',"mouse_scroll",GuiMouseScrollEvent.class), +// GUI_CLOSE('e',"close",GuiCloseEvent.class); private Class ev; private char ch; diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java index a240052..0e34fba 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java @@ -28,6 +28,8 @@ public class BlockBreakEvent extends RecordEvent { } public void replay() { - Main.client.interactionManager.breakBlock(pos); + if (Main.client.interactionManager != null) { + Main.client.interactionManager.breakBlock(pos); + } } } diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java b/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java index 4eb99d1..bdae075 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java @@ -33,7 +33,9 @@ public class BlockInteractEvent extends RecordEvent { } public void replay() { - Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); + if (Main.client.interactionManager != null) { + Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); + } } protected String[] serializeArgs() { diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java new file mode 100644 index 0000000..4428984 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java @@ -0,0 +1,34 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiCharTypeEvent extends RecordEvent { + private char chr; + private int modifiers; + + public GuiCharTypeEvent(char chr, int modifiers) { + this.chr = chr; + this.modifiers = modifiers; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.charTyped(chr, modifiers); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf((int) chr), + String.valueOf(modifiers) + }; + } + + public static GuiCharTypeEvent deserialize(String[] args) { + return new GuiCharTypeEvent( + (char) Integer.parseInt(args[0]), + Integer.parseInt(args[1]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java new file mode 100644 index 0000000..f2c3024 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java @@ -0,0 +1,22 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiCloseEvent extends RecordEvent { + public GuiCloseEvent() {} + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.setScreen(null); + } + } + + protected String[] serializeArgs() { + return new String[] {}; + } + + public static GuiCloseEvent deserialize(String[] args) { + return new GuiCloseEvent(); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java new file mode 100644 index 0000000..cf5221f --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java @@ -0,0 +1,38 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiKeyPressEvent extends RecordEvent { + private int keyCode; + private int scanCode; + private int modifiers; + + public GuiKeyPressEvent(int keyCode, int scanCode, int modifiers) { + this.keyCode = keyCode; + this.scanCode = scanCode; + this.modifiers = modifiers; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.keyPressed(keyCode, scanCode, modifiers); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(keyCode), + String.valueOf(scanCode), + String.valueOf(modifiers) + }; + } + + public static GuiKeyPressEvent deserialize(String[] args) { + return new GuiKeyPressEvent( + Integer.parseInt(args[0]), + Integer.parseInt(args[1]), + Integer.parseInt(args[2]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java new file mode 100644 index 0000000..8134c73 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java @@ -0,0 +1,38 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiKeyReleaseEvent extends RecordEvent { + private int keyCode; + private int scanCode; + private int modifiers; + + public GuiKeyReleaseEvent(int keyCode, int scanCode, int modifiers) { + this.keyCode = keyCode; + this.scanCode = scanCode; + this.modifiers = modifiers; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.keyReleased(keyCode, scanCode, modifiers); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(keyCode), + String.valueOf(scanCode), + String.valueOf(modifiers) + }; + } + + public static GuiKeyReleaseEvent deserialize(String[] args) { + return new GuiKeyReleaseEvent( + Integer.parseInt(args[0]), + Integer.parseInt(args[1]), + Integer.parseInt(args[2]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java new file mode 100644 index 0000000..1c47776 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java @@ -0,0 +1,38 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiMouseClickEvent extends RecordEvent { + private double mouseX; + private double mouseY; + private int button; + + public GuiMouseClickEvent(double mouseX, double mouseY, int button) { + this.mouseX = mouseX; + this.mouseY = mouseY; + this.button = button; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.mouseClicked(mouseX, mouseY, button); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(mouseX), + String.valueOf(mouseY), + String.valueOf(button) + }; + } + + public static GuiMouseClickEvent deserialize(String[] args) { + return new GuiMouseClickEvent( + Double.parseDouble(args[0]), + Double.parseDouble(args[1]), + Integer.parseInt(args[2]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java new file mode 100644 index 0000000..f351482 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java @@ -0,0 +1,46 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiMouseDragEvent extends RecordEvent { + private double mouseX; + private double mouseY; + private double deltaX; + private double deltaY; + private int button; + + public GuiMouseDragEvent(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + this.mouseX = mouseX; + this.mouseY = mouseY; + this.deltaX = deltaX; + this.deltaY = deltaY; + this.button = button; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(mouseX), + String.valueOf(mouseY), + String.valueOf(button), + String.valueOf(deltaX), + String.valueOf(deltaY) + }; + } + + public static GuiMouseDragEvent deserialize(String[] args) { + return new GuiMouseDragEvent( + Double.parseDouble(args[0]), + Double.parseDouble(args[1]), + Integer.parseInt(args[2]), + Double.parseDouble(args[3]), + Double.parseDouble(args[4]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java new file mode 100644 index 0000000..7da1c82 --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java @@ -0,0 +1,34 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiMouseMoveEvent extends RecordEvent { + private double mouseX; + private double mouseY; + + public GuiMouseMoveEvent(double mouseX, double mouseY) { + this.mouseX = mouseX; + this.mouseY = mouseY; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.mouseMoved(mouseX, mouseY); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(mouseX), + String.valueOf(mouseY) + }; + } + + public static GuiMouseMoveEvent deserialize(String[] args) { + return new GuiMouseMoveEvent( + Double.parseDouble(args[0]), + Double.parseDouble(args[1]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java new file mode 100644 index 0000000..63b8b6b --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java @@ -0,0 +1,38 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiMouseReleaseEvent extends RecordEvent { + private double mouseX; + private double mouseY; + private int button; + + public GuiMouseReleaseEvent(double mouseX, double mouseY, int button) { + this.mouseX = mouseX; + this.mouseY = mouseY; + this.button = button; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.mouseReleased(mouseX, mouseY, button); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(mouseX), + String.valueOf(mouseY), + String.valueOf(button) + }; + } + + public static GuiMouseReleaseEvent deserialize(String[] args) { + return new GuiMouseReleaseEvent( + Double.parseDouble(args[0]), + Double.parseDouble(args[1]), + Integer.parseInt(args[2]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java new file mode 100644 index 0000000..470c29c --- /dev/null +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java @@ -0,0 +1,38 @@ +package themixray.repeating.mod.event.events; + +import themixray.repeating.mod.Main; +import themixray.repeating.mod.event.RecordEvent; + +public class GuiMouseScrollEvent extends RecordEvent { + private double mouseX; + private double mouseY; + private double amount; + + public GuiMouseScrollEvent(double mouseX, double mouseY, double amount) { + this.mouseX = mouseX; + this.mouseY = mouseY; + this.amount = amount; + } + + public void replay() { + if (Main.client.currentScreen != null) { + Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount); + } + } + + protected String[] serializeArgs() { + return new String[] { + String.valueOf(mouseX), + String.valueOf(mouseY), + String.valueOf(amount) + }; + } + + public static GuiMouseScrollEvent deserialize(String[] args) { + return new GuiMouseScrollEvent( + Double.parseDouble(args[0]), + Double.parseDouble(args[1]), + Double.parseDouble(args[2]) + ); + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java b/src/main/java/themixray/repeating/mod/event/events/InputEvent.java index 19c8b66..9ca3128 100644 --- a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/InputEvent.java @@ -116,33 +116,35 @@ public class InputEvent extends RecordEvent { } public void inputCallback() { - if (sprinting != null && Main.client.player.isSprinting() != sprinting) - Main.client.player.setSprinting(sprinting); - if (Main.client.player.getYaw() != yaw) - Main.client.player.setYaw(yaw); - if (Main.client.player.getHeadYaw() != head_yaw) - Main.client.player.setHeadYaw(head_yaw); - if (Main.client.player.getBodyYaw() != body_yaw) - Main.client.player.setBodyYaw(body_yaw); - if (Main.client.player.getPitch() != pitch) - Main.client.player.setPitch(pitch); - if (Main.client.player.getMovementSpeed() != speed) - Main.client.player.setMovementSpeed(speed); - if (sneaking != null && Main.client.player.input.sneaking != sneaking) - Main.client.player.input.sneaking = sneaking; - if (jumping != null && Main.client.player.input.jumping != jumping) - Main.client.player.input.jumping = jumping; - if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) - Main.client.player.input.movementSideways = movementSideways; - if (movementForward != null && Main.client.player.input.movementForward != movementForward) - Main.client.player.input.movementForward = movementForward; - if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) - Main.client.player.input.pressingForward = pressingForward; - if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) - Main.client.player.input.pressingBack = pressingBack; - if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) - Main.client.player.input.pressingLeft = pressingLeft; - if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) - Main.client.player.input.pressingRight = pressingRight; + if (Main.client.player != null) { + if (sprinting != null && Main.client.player.isSprinting() != sprinting) + Main.client.player.setSprinting(sprinting); + if (Main.client.player.getYaw() != yaw) + Main.client.player.setYaw(yaw); + if (Main.client.player.getHeadYaw() != head_yaw) + Main.client.player.setHeadYaw(head_yaw); + if (Main.client.player.getBodyYaw() != body_yaw) + Main.client.player.setBodyYaw(body_yaw); + if (Main.client.player.getPitch() != pitch) + Main.client.player.setPitch(pitch); + if (Main.client.player.getMovementSpeed() != speed) + Main.client.player.setMovementSpeed(speed); + if (sneaking != null && Main.client.player.input.sneaking != sneaking) + Main.client.player.input.sneaking = sneaking; + if (jumping != null && Main.client.player.input.jumping != jumping) + Main.client.player.input.jumping = jumping; + if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) + Main.client.player.input.movementSideways = movementSideways; + if (movementForward != null && Main.client.player.input.movementForward != movementForward) + Main.client.player.input.movementForward = movementForward; + if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) + Main.client.player.input.pressingForward = pressingForward; + if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) + Main.client.player.input.pressingBack = pressingBack; + if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) + Main.client.player.input.pressingLeft = pressingLeft; + if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) + Main.client.player.input.pressingRight = pressingRight; + } } } diff --git a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java b/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java index 9957dbf..c8b8bf1 100644 --- a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java @@ -26,11 +26,13 @@ public class MoveEvent extends RecordEvent { } public void replay() { - Vec3d p = Main.client.player.getPos(); - Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); - Main.client.player.move(MovementType.SELF, v); - Main.client.player.setYaw(yaw); - Main.client.player.setPitch(pitch); + if (Main.client.player != null) { + Vec3d p = Main.client.player.getPos(); + Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); + Main.client.player.move(MovementType.SELF, v); + Main.client.player.setYaw(yaw); + Main.client.player.setPitch(pitch); + } } protected String[] serializeArgs() { diff --git a/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java b/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java index 566b520..67735f9 100644 --- a/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java @@ -18,6 +18,8 @@ import java.util.UUID; public abstract class PlayerMixin { @Inject(at = @At(value = "HEAD"), method = "disconnect") private void disconnect(Text disconnectReason, CallbackInfo ci) { - System.out.println("on client close"); + if (Main.me.is_replaying) { + Main.me.stopReplay(); + } } } diff --git a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java index 1dd91e8..25b9504 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java @@ -12,53 +12,79 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import themixray.repeating.mod.Main; import themixray.repeating.mod.TickTask; +import themixray.repeating.mod.event.events.*; @Mixin(Screen.class) public abstract class ScreenMixin extends AbstractParentElement implements Drawable { @Inject(at = @At(value = "HEAD"), method = "close") private void close(CallbackInfo ci) { - System.out.println("on screen close"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiCloseEvent()); + } } @Inject(at = @At(value = "HEAD"), method = "keyPressed") private void keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { - System.out.println("on screen keyPressed"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiKeyPressEvent(keyCode, scanCode, modifiers)); + } } @Override public boolean charTyped(char chr, int modifiers) { - System.out.println("on screen charTyped"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiCharTypeEvent(chr, modifiers)); + } return super.charTyped(chr, modifiers); } @Override public boolean keyReleased(int keyCode, int scanCode, int modifiers) { - System.out.println("on screen keyReleased"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiKeyReleaseEvent(keyCode, scanCode, modifiers)); + } return super.keyReleased(keyCode, scanCode, modifiers); } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - System.out.println("on screen mouseClicked"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiMouseClickEvent(mouseX, mouseY, button)); + } return super.mouseClicked(mouseX, mouseY, button); } + @Override + public void mouseMoved(double mouseX, double mouseY) { + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiMouseMoveEvent(mouseX, mouseY)); + } + super.mouseMoved(mouseX, mouseY); + } + @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - System.out.println("on screen mouseDragged"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiMouseDragEvent(mouseX, mouseY, button, deltaX, deltaY)); + } return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); } @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { - System.out.println("on screen mouseReleased"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiMouseReleaseEvent(mouseX, mouseY, button)); + } return super.mouseReleased(mouseX, mouseY, button); } @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - System.out.println("on screen mouseScrolled"); + if (Main.me.is_recording) { + Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount)); + } return super.mouseScrolled(mouseX, mouseY, amount); } } From e5981e6e60a48f6b40b94ecc2dc60c22828ea22d Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 20:38:34 +0300 Subject: [PATCH 40/72] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d88726..a562e49 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Releases dont have striked lines - ~~select record on click (you can see start and finish points when its selected)~~ - ~~select record on import~~ - ~~change icon in mod menu to new one~~ -- fix "Crashes the game when leaving world while record is replaying" +- ~~fix "Crashes the game when leaving world while record is replaying"~~ - record gui mouse clicks and key pressing - create new preview.gif - copy file to record list when import From e808b6ca095a4cabf41859a3d6053adf92faa4e9 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Tue, 23 Apr 2024 20:43:49 +0300 Subject: [PATCH 41/72] import now copies file to your record folder --- src/main/java/themixray/repeating/mod/RecordList.java | 9 +++++++++ .../java/themixray/repeating/mod/RepeatingScreen.java | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index cb93741..9e2fc91 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -5,6 +5,7 @@ import themixray.repeating.mod.widget.RecordListWidget; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -76,4 +77,12 @@ public class RecordList { return state; } + + public RecordState cloneRecord(File file) throws Exception { + File out = new File(Main.me.records_folder, file.getName()); + Files.copy(file.toPath(), out.toPath()); + RecordState state = RecordState.load(out); + addRecord(state); + return state; + } } diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index b610d74..0fcd5ec 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -148,7 +148,7 @@ public class RepeatingScreen extends Screen { if (files != null) { for (File file : files) { try { - Main.me.setNowRecord(Main.me.record_list.addRecord(file)); + Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); } catch (Exception e) { throw new RuntimeException(e); } From 1ceea8bcd42f7d973106e797d0bc042f52fcd9ab Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Tue, 23 Apr 2024 20:44:56 +0300 Subject: [PATCH 42/72] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a562e49..e2be31c 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Releases dont have striked lines - ~~select record on import~~ - ~~change icon in mod menu to new one~~ - ~~fix "Crashes the game when leaving world while record is replaying"~~ +- ~~copy file to record list when import~~ - record gui mouse clicks and key pressing - create new preview.gif -- copy file to record list when import - practice mode like in geometry dash for parkours ## How to build From 73154eff2bd8878b8d1b16dd09d7654a2936dc6a Mon Sep 17 00:00:00 2001 From: MeexReay Date: Fri, 26 Apr 2024 17:54:43 +0300 Subject: [PATCH 43/72] sort records by last modified + fix errors --- .../java/themixray/repeating/mod/Main.java | 7 ++++++- .../themixray/repeating/mod/RecordList.java | 14 ++++++++------ .../themixray/repeating/mod/RecordState.java | 2 +- .../repeating/mod/event/RecordEvent.java | 2 +- .../repeating/mod/mixin/ScreenMixin.java | 18 +++++++++--------- .../mod/widget/RecordListWidget.java | 3 ++- .../repeating/mod/widget/RecordWidget.java | 19 ++++++++++++++++++- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/themixray/repeating/mod/Main.java index 37e5e28..c2b8645 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/themixray/repeating/mod/Main.java @@ -316,7 +316,12 @@ public class Main implements ClientModInitializer { replay_tick.cancel(); replay_tick = null; } - menu.updateButtons(); + try { + now_record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + menu.updateButtons(); record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); sendMessage(Text.translatable("message.repeating-mod.replay_stop")); } diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index 9e2fc91..99fffe7 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -7,18 +7,16 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; public class RecordList { private final File folder; - private List records; + private LinkedList records; private RecordListWidget widget; public RecordList(File folder) { this.folder = folder; - this.records = new ArrayList<>(); + this.records = new LinkedList<>(); this.widget = new RecordListWidget(0, 0, 180, 200); } @@ -35,7 +33,11 @@ public class RecordList { } public void loadRecords() { - for (File file : folder.listFiles()) { + LinkedList files = new LinkedList<>(List.of(folder.listFiles())); + + files.sort(Comparator.comparingLong((f) -> f.lastModified())); + + for (File file : files) { try { addRecord(file); } catch (Exception e) {} diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index 2272f03..21d782b 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -101,7 +101,7 @@ public class RecordState { public RecordEvent getLastEvent(String type) { for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { - if (r.getType().getName().equals(type)) { + if (r.getType() != null && r.getType().getName().equals(type)) { return r; } } diff --git a/src/main/java/themixray/repeating/mod/event/RecordEvent.java b/src/main/java/themixray/repeating/mod/event/RecordEvent.java index 7b56dc7..0c2cb64 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEvent.java +++ b/src/main/java/themixray/repeating/mod/event/RecordEvent.java @@ -6,7 +6,7 @@ public abstract class RecordEvent { public abstract void replay(); public RecordEventType getType() { for (RecordEventType ev : RecordEventType.values()) { - if (ev.getEventClass().equals(this.getClass())) { + if (ev.getEventClass().getTypeName().equals(this.getClass().getTypeName())) { return ev; } } diff --git a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java index 25b9504..bfa46c8 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java @@ -21,21 +21,21 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Inject(at = @At(value = "HEAD"), method = "close") private void close(CallbackInfo ci) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiCloseEvent()); +// Main.me.now_record.addEvent(new GuiCloseEvent()); } } @Inject(at = @At(value = "HEAD"), method = "keyPressed") private void keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiKeyPressEvent(keyCode, scanCode, modifiers)); +// Main.me.now_record.addEvent(new GuiKeyPressEvent(keyCode, scanCode, modifiers)); } } @Override public boolean charTyped(char chr, int modifiers) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiCharTypeEvent(chr, modifiers)); +// Main.me.now_record.addEvent(new GuiCharTypeEvent(chr, modifiers)); } return super.charTyped(chr, modifiers); } @@ -43,7 +43,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public boolean keyReleased(int keyCode, int scanCode, int modifiers) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiKeyReleaseEvent(keyCode, scanCode, modifiers)); +// Main.me.now_record.addEvent(new GuiKeyReleaseEvent(keyCode, scanCode, modifiers)); } return super.keyReleased(keyCode, scanCode, modifiers); } @@ -51,7 +51,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiMouseClickEvent(mouseX, mouseY, button)); +// Main.me.now_record.addEvent(new GuiMouseClickEvent(mouseX, mouseY, button)); } return super.mouseClicked(mouseX, mouseY, button); } @@ -59,7 +59,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public void mouseMoved(double mouseX, double mouseY) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiMouseMoveEvent(mouseX, mouseY)); +// Main.me.now_record.addEvent(new GuiMouseMoveEvent(mouseX, mouseY)); } super.mouseMoved(mouseX, mouseY); } @@ -67,7 +67,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiMouseDragEvent(mouseX, mouseY, button, deltaX, deltaY)); +// Main.me.now_record.addEvent(new GuiMouseDragEvent(mouseX, mouseY, button, deltaX, deltaY)); } return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); } @@ -75,7 +75,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public boolean mouseReleased(double mouseX, double mouseY, int button) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiMouseReleaseEvent(mouseX, mouseY, button)); +// Main.me.now_record.addEvent(new GuiMouseReleaseEvent(mouseX, mouseY, button)); } return super.mouseReleased(mouseX, mouseY, button); } @@ -83,7 +83,7 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa @Override public boolean mouseScrolled(double mouseX, double mouseY, double amount) { if (Main.me.is_recording) { - Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount)); +// Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount)); } return super.mouseScrolled(mouseX, mouseY, amount); } diff --git a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java index ddded9f..d038f91 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java @@ -14,11 +14,12 @@ import themixray.repeating.mod.RecordState; import themixray.repeating.mod.RepeatingScreen; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; public class RecordListWidget extends ScrollableWidget { - private List widgets = new ArrayList<>(); + private LinkedList widgets = new LinkedList<>(); private boolean focused = false; public RecordListWidget(int x, int y, int width, int height) { diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java index 44d3e23..3838168 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -6,6 +6,8 @@ import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.*; import net.minecraft.text.Style; import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.Formatting; import themixray.repeating.mod.Main; import themixray.repeating.mod.RecordState; import themixray.repeating.mod.RenderListener; @@ -43,7 +45,10 @@ public class RecordWidget implements Drawable, Widget { } public boolean contains(int x, int y) { - return parent.getX() + getX() <= x && parent.getY() + getY() <= y && x <= parent.getX() + getX() + getWidth() && y <= parent.getY() + getY() + getHeight(); + return parent.getX() + getX() <= x && + parent.getY() + getY() <= y && + x <= parent.getX() + getX() + getWidth() && + y <= parent.getY() + getY() + getHeight(); } public void setX(int x) { @@ -105,6 +110,11 @@ public class RecordWidget implements Drawable, Widget { children.add(delete_button); ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> { + try { + record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } if (Desktop.isDesktopSupported()) { Desktop desk = Desktop.getDesktop(); try { @@ -116,6 +126,9 @@ public class RecordWidget implements Drawable, Widget { throw new RuntimeException(ex); } } + } else { + Main.sendMessage(Text.literal("Record file is ").append( + Text.literal("["+record.getFile().getAbsolutePath()+"]").styled((s) -> s.withColor(Formatting.GRAY)))); } }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build(); @@ -124,11 +137,15 @@ public class RecordWidget implements Drawable, Widget { ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.start"), (i) -> { if (Main.me.is_replaying) { Main.me.stopReplay(); + if (getRecord().equals(Main.me.now_record)) { + return; + } } i.setMessage(Text.translatable("text.repeating-mod.stop")); Main.me.now_record = record; Main.me.startReplay(); + Main.client.setScreen(null); }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); From 41706ade8010fa60982d9681839252bc3eb9c39a Mon Sep 17 00:00:00 2001 From: MeexReay <127148610+MeexReay@users.noreply.github.com> Date: Fri, 26 Apr 2024 20:08:14 +0300 Subject: [PATCH 44/72] Update README.md --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index e2be31c..f63c38a 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,7 @@ How menu looks like ## Todo Releases dont have striked lines -- ~~fix "Space in record name deletes record 0_0"~~ -- ~~fix "Save record (edit name) on recording crashes the game"~~ -- ~~select record on click (you can see start and finish points when its selected)~~ -- ~~select record on import~~ -- ~~change icon in mod menu to new one~~ -- ~~fix "Crashes the game when leaving world while record is replaying"~~ -- ~~copy file to record list when import~~ -- record gui mouse clicks and key pressing +- record gui mouse clicks and key pressing (in dev) - create new preview.gif - practice mode like in geometry dash for parkours From cc12eab00619f6470b9c3f4914ef1274cb44cfe9 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 8 Dec 2024 02:39:49 +0300 Subject: [PATCH 45/72] wh --- gradle.properties | 32 +- gradlew.bat | 184 ++--- .../themixray/repeating/mod/EasyConfig.java | 103 --- remappedSrc/themixray/repeating/mod/Main.java | 325 --------- .../themixray/repeating/mod/RecordList.java | 77 -- .../themixray/repeating/mod/RecordState.java | 169 ----- .../repeating/mod/RenderListener.java | 11 - .../repeating/mod/RepeatingScreen.java | 193 ----- .../themixray/repeating/mod/TickTask.java | 94 --- .../mod/event/RecordBlockBreakEvent.java | 32 - .../mod/event/RecordBlockInteractEvent.java | 46 -- .../repeating/mod/event/RecordDelayEvent.java | 29 - .../repeating/mod/event/RecordEvent.java | 28 - .../repeating/mod/event/RecordInputEvent.java | 147 ---- .../repeating/mod/event/RecordMoveEvent.java | 42 -- .../repeating/mod/mixin/ClientMixin.java | 24 - .../repeating/mod/mixin/EntityMixin.java | 31 - .../repeating/mod/mixin/InputMixin.java | 20 - .../repeating/mod/mixin/MovementMixin.java | 44 -- .../repeating/mod/mixin/NetworkMixin.java | 29 - .../repeating/mod/mixin/RendererMixin.java | 21 - .../repeating/mod/render/RenderHelper.java | 200 ------ .../repeating/mod/render/RenderSystem.java | 13 - .../mod/render/buffer/BufferManager.java | 48 -- .../repeating/mod/render/buffer/Vertex.java | 30 - .../mod/render/buffer/WorldBuffer.java | 82 --- .../repeating/mod/render/shader/Shader.java | 42 -- .../mod/render/shader/ShaderManager.java | 97 --- .../mod/widget/RecordListWidget.java | 150 ---- .../repeating/mod/widget/RecordWidget.java | 196 ----- .../themixray/repeating/mod/EasyConfig.java | 206 +++--- .../java/themixray/repeating/mod/Main.java | 680 +++++++++--------- .../themixray/repeating/mod/RecordList.java | 1 + .../themixray/repeating/mod/RecordState.java | 6 +- .../repeating/mod/RepeatingScreen.java | 374 +++++----- .../themixray/repeating/mod/TickTask.java | 190 ++--- .../mod/event/events/GuiMouseScrollEvent.java | 2 +- .../repeating/mod/mixin/ClientMixin.java | 48 +- .../repeating/mod/mixin/EntityMixin.java | 62 +- .../repeating/mod/mixin/InputMixin.java | 40 +- .../repeating/mod/mixin/MovementMixin.java | 88 +-- .../repeating/mod/mixin/NetworkMixin.java | 58 +- .../repeating/mod/mixin/RendererMixin.java | 42 +- .../repeating/mod/mixin/ScreenMixin.java | 14 +- .../repeating/mod/widget/RecordWidget.java | 5 +- .../assets/minecraft/font/default.json | 32 +- .../assets/repeating-mod/lang/en_us.json | 70 +- .../assets/repeating-mod/lang/ru_ru.json | 76 +- src/main/resources/fabric.mod.json | 76 +- src/main/resources/repeating-mod.mixins.json | 40 +- 50 files changed, 1166 insertions(+), 3483 deletions(-) delete mode 100644 remappedSrc/themixray/repeating/mod/EasyConfig.java delete mode 100644 remappedSrc/themixray/repeating/mod/Main.java delete mode 100644 remappedSrc/themixray/repeating/mod/RecordList.java delete mode 100644 remappedSrc/themixray/repeating/mod/RecordState.java delete mode 100644 remappedSrc/themixray/repeating/mod/RenderListener.java delete mode 100644 remappedSrc/themixray/repeating/mod/RepeatingScreen.java delete mode 100644 remappedSrc/themixray/repeating/mod/TickTask.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/InputMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/RenderHelper.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/RenderSystem.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/shader/Shader.java delete mode 100644 remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java delete mode 100644 remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java delete mode 100644 remappedSrc/themixray/repeating/mod/widget/RecordWidget.java diff --git a/gradle.properties b/gradle.properties index 4fe0e23..be55ec6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,17 @@ -# Done to increase the memory available to gradle. -org.gradle.jvmargs=-Xmx1G -org.gradle.parallel=true - -# Fabric Properties -# check these on https://fabricmc.net/develop -minecraft_version=1.20 -yarn_mappings=1.20+build.1 -loader_version=0.15.10 - -#Fabric api -fabric_version=0.83.0+1.20 - -# Mod Properties -mod_version = 1.1.1+1.20 -maven_group = themixray.repeating.mod +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.20.1 +yarn_mappings=1.20.1+build.3 +loader_version=0.15.10 + +#Fabric api +fabric_version=0.97.0+1.20.4 + +# Mod Properties +mod_version = 1.1.2+1.20.1 +maven_group = themixray.repeating.mod archives_base_name = repeating-mod \ No newline at end of file diff --git a/gradlew.bat b/gradlew.bat index 25da30d..7101f8e 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/remappedSrc/themixray/repeating/mod/EasyConfig.java b/remappedSrc/themixray/repeating/mod/EasyConfig.java deleted file mode 100644 index 8836722..0000000 --- a/remappedSrc/themixray/repeating/mod/EasyConfig.java +++ /dev/null @@ -1,103 +0,0 @@ -package themixray.repeating.mod; - -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(File f, Map def) { - this.path = f.toPath(); - this.file = f; - this.data = new HashMap<>(); - - if (!file.exists()) { - try { - file.createNewFile(); - write(def); - } catch (IOException e) { - e.printStackTrace(); - } - } - - reload(); - - for (Map.Entry m:def.entrySet()) - if (!data.containsKey(m.getKey())) - data.put(m.getKey(),m.getValue()); - - save(); - } - public EasyConfig(Path f, Map def) { - this(f.toFile(),def); - } - public EasyConfig(String parent,String child,Map def) { - this(new File(parent,child),def); - } - public EasyConfig(File parent,String child,Map def) { - this(new File(parent,child),def); - } - public EasyConfig(Path parent,String child,Map def) { - this(new File(parent.toFile(),child),def); - } - - public EasyConfig(File f) { - this(f,new HashMap<>()); - } - public EasyConfig(Path path) { - this(path.toFile(),new HashMap<>()); - } - public EasyConfig(String parent,String child) { - this(new File(parent,child),new HashMap<>()); - } - public EasyConfig(File parent,String child) { - this(new File(parent,child),new HashMap<>()); - } - public EasyConfig(Path parent,String child) { - this(new File(parent.toFile(),child),new HashMap<>()); - } - - public void reload() { - data = read(); - } - public void save() { - write(data); - } - - private String toText(Map p) { - StringBuilder t = new StringBuilder(); - for (Map.Entry e:p.entrySet()) - t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); - return t.toString(); - } - private Map toMap(String j) { - Map m = new HashMap<>(); - for (String l:j.split("\n")) { - String s[] = l.split("="); - m.put(s[0],s[1]); - } - return m; - } - - 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, toText(p).getBytes()); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/remappedSrc/themixray/repeating/mod/Main.java b/remappedSrc/themixray/repeating/mod/Main.java deleted file mode 100644 index 6a851ea..0000000 --- a/remappedSrc/themixray/repeating/mod/Main.java +++ /dev/null @@ -1,325 +0,0 @@ -package themixray.repeating.mod; - -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.option.KeyBinding; -import net.minecraft.client.util.InputUtil; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.Vec3d; -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import themixray.repeating.mod.event.RecordDelayEvent; -import themixray.repeating.mod.event.RecordEvent; -import themixray.repeating.mod.event.RecordInputEvent; -import themixray.repeating.mod.event.RecordMoveEvent; -import themixray.repeating.mod.render.RenderHelper; -import themixray.repeating.mod.render.RenderSystem; -import themixray.repeating.mod.render.buffer.WorldBuffer; - -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.util.*; - -public class Main implements ClientModInitializer { - public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); - public static final MinecraftClient client = MinecraftClient.getInstance(); - public static final FabricLoader loader = FabricLoader.getInstance(); - public static Main me; - - public RecordList record_list; - public RecordState now_record; - - public boolean is_recording = false; - public long last_record = -1; - public TickTask move_tick = null; - - public TickTask replay_tick = null; - public boolean is_replaying = false; - public boolean loop_replay = false; - public static RecordInputEvent input_replay = null; - - public long living_ticks = 0; - - public static RepeatingScreen menu; - private static KeyBinding menu_key; - private static KeyBinding toggle_replay_key; - private static KeyBinding toggle_record_key; - - public long record_pos_delay = 20; - - public static Random rand = new Random(); - - public EasyConfig conf; - public File records_folder; - - @Override - public void onInitializeClient() { - LOGGER.info("Repeating mod initialized"); - me = this; - - now_record = null; - - records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); - if (!records_folder.exists()) records_folder.mkdir(); - - record_list = new RecordList(records_folder); - record_list.loadRecords(); - - RenderSystem.init(); - WorldRenderEvents.LAST.register(context -> { - WorldBuffer buffer = RenderHelper.startTri(context); - if (now_record != null) { - Vec3d start_pos = now_record.getStartRecordPos(); - Vec3d finish_pos = now_record.getFinishRecordPos(); - - if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128)); - if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128)); - } - RenderHelper.endTri(buffer); - }); - - Map def = new HashMap<>(); - def.put("record_pos_delay", String.valueOf(record_pos_delay)); - - conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); - - record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); - - menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.menu",InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_J,"text.repeating-mod.name")); - toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, - -1,"text.repeating-mod.name")); - toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, - -1,"text.repeating-mod.name")); - - menu = new RepeatingScreen(); - ClientTickEvents.END_CLIENT_TICK.register(client -> { - if (menu_key.wasPressed()) - client.setScreen(menu); - if (toggle_replay_key.wasPressed()) { - if (now_record != null) { - if (!is_recording) { - if (is_replaying) - stopReplay(); - else startReplay(); - menu.updateButtons(); - } - } - } - if (toggle_record_key.wasPressed()) { - if (!is_replaying) { - if (is_recording) - stopRecording(); - else startRecording(); - menu.updateButtons(); - } - } - }); - - new TickTask(0,0) { - @Override - public void run() { - living_ticks++; - } - }; - - System.setProperty("java.awt.headless", "false"); - } - - public void setNowRecord(RecordState record) { - now_record = record; - } - - public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { - RenderHelper.drawRectFromTri(buffer, - (float) pos.getX() - 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() - 0.25F, - - (float) pos.getX() + 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() - 0.25F, - - (float) pos.getX() + 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() + 0.25F, - - (float) pos.getX() - 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() + 0.25F, - color); - } - - public void startRecording() { - is_recording = true; - menu.updateButtons(); - - now_record = record_list.newRecord(); - - Vec3d start_pos = client.player.getPos(); - now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); - now_record.setStartRecordPos(start_pos); - - if (record_pos_delay > 0) { - move_tick = new TickTask( - record_pos_delay, - record_pos_delay) { - @Override - public void run() { - now_record.addEvent(new RecordMoveEvent(client.player.getPos(), - client.player.getHeadYaw(), client.player.getPitch())); - } - }; - } - - sendMessage(Text.translatable("message.repeating-mod.record_start")); - } - - public void recordTick(RecordEvent e) { - if (is_recording) { - long now = living_ticks; - if (last_record != -1) { - long diff = now - last_record - 2; - if (diff > 0) now_record.addEvent(new RecordDelayEvent(diff)); - } - now_record.addEvent(e); - last_record = now; - } - } - - public void recordAllInput() { - if (client.player == null) { - stopRecording(); - return; - } - - RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input")); - if (l == null) { - RecordInputEvent e = new RecordInputEvent( - client.player.input.sneaking, - client.player.input.jumping, - client.player.input.movementSideways, - client.player.input.movementForward, - client.player.input.pressingForward, - client.player.input.pressingBack, - client.player.input.pressingLeft, - client.player.input.pressingRight, - client.player.getHeadYaw(), - client.player.getBodyYaw(), - client.player.getPitch(), - client.player.isSprinting(), - client.player.getYaw(), - client.player.getMovementSpeed()); - recordTick(e); - } else { - RecordInputEvent e = new RecordInputEvent( - ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, - ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, - (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, - (((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, - ((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, - ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, - ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, - ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, - client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), - ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), - client.player.getYaw(),client.player.getMovementSpeed()); - - if (!(e.isEmpty() && - e.yaw == l.yaw && - e.head_yaw == l.head_yaw && - e.pitch == l.pitch && - e.body_yaw == l.body_yaw)) { - e.fillEmpty(l); - recordTick(e); - } - } - } - - public void stopRecording() { - is_recording = false; - now_record.setFinishRecordPos(client.player.getPos()); - try { - now_record.save(); - } catch (IOException e) { - throw new RuntimeException(e); - } - if (move_tick != null) { - move_tick.cancel(); - move_tick = null; - } - menu.updateButtons(); - last_record = -1; - sendMessage(Text.translatable("message.repeating-mod.record_stop")); - } - - - public void startReplay() { - is_recording = false; - is_replaying = true; - menu.updateButtons(); - - List events = now_record.getEvents(); - - replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { - public int replay_index = 0; - - @Override - public void run() { - if (!is_replaying) cancel(); - RecordEvent e = events.get(replay_index); - if (e instanceof RecordDelayEvent) { - setDelay(((RecordDelayEvent) e).delay); - } else { - e.replay(); - } - - replay_index++; - if (!loop_replay) { - if (replay_index == events.size()) { - stopReplay(); - cancel(); - } - } else if (replay_index == events.size()) { - replay_index = 0; - } - } - }; - - sendMessage(Text.translatable("message.repeating-mod.replay_start")); - } - - public void stopReplay() { - is_recording = false; - is_replaying = false; - if (replay_tick != null) { - replay_tick.cancel(); - replay_tick = null; - } - menu.updateButtons(); - record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); - sendMessage(Text.translatable("message.repeating-mod.replay_stop")); - } - - public static void sendMessage(MutableText text) { - client.player.sendMessage(Text.literal("[") - .append(Text.translatable("text.repeating-mod.name")) - .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) - .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); - } - - public static void sendDebug(String s) { - client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); - } -} diff --git a/remappedSrc/themixray/repeating/mod/RecordList.java b/remappedSrc/themixray/repeating/mod/RecordList.java deleted file mode 100644 index e17a0e8..0000000 --- a/remappedSrc/themixray/repeating/mod/RecordList.java +++ /dev/null @@ -1,77 +0,0 @@ -package themixray.repeating.mod; - -import net.minecraft.text.Text; -import themixray.repeating.mod.widget.RecordListWidget; - -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class RecordList { - private final File folder; - private List records; - private RecordListWidget widget; - - public RecordList(File folder) { - this.folder = folder; - this.records = new ArrayList<>(); - this.widget = new RecordListWidget(0, 0, 180, 200); - } - - public List getRecords() { - return records; - } - - public File getFolder() { - return folder; - } - - public RecordListWidget getWidget() { - return widget; - } - - public void loadRecords() { - for (File file : folder.listFiles()) { - try { - addRecord(file); - } catch (Exception e) {} - } - } - - public void addRecord(File file) throws Exception { - addRecord(RecordState.load(file)); - } - - public void addRecord(RecordState record) { - records.add(record); - widget.addWidget(record); - } - - public void removeRecord(RecordState record) { - records.remove(record); - widget.removeWidget(record); - } - - public RecordState newRecord() { - Date date = new Date(); - String name = "Unnamed"; - String author = Main.client.player.getName().getString(); - - File file = new File(Main.me.records_folder, - "record_" + RecordState.FILE_DATE_FORMAT.format(date) + - "_" + Main.rand.nextInt(10) + ".rrm"); - - RecordState state = new RecordState( - file, name, date, author, - new ArrayList<>(), - null, - null); - - addRecord(state); - - return state; - } -} diff --git a/remappedSrc/themixray/repeating/mod/RecordState.java b/remappedSrc/themixray/repeating/mod/RecordState.java deleted file mode 100644 index 239194a..0000000 --- a/remappedSrc/themixray/repeating/mod/RecordState.java +++ /dev/null @@ -1,169 +0,0 @@ -package themixray.repeating.mod; - -import com.google.common.collect.Lists; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.event.RecordEvent; - -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class RecordState { - public static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM.dd.yyyy HH:mm:ss"); - public static SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("MM-dd-yyyy_HH-mm-ss"); - - private final File file; - private String name; - private Date date; - private String author; - - private List events; - private Vec3d start_record_pos; - private Vec3d finish_record_pos; - - public RecordState(File file, - String name, - Date date, - String author, - List events, - Vec3d start_record_pos, - Vec3d finish_record_pos) { - this.file = file; - this.name = name; - this.date = date; - this.author = author; - - this.events = events; - this.start_record_pos = start_record_pos; - this.finish_record_pos = finish_record_pos; - } - - public File getFile() { - return file; - } - - public String getName() { - return name; - } - - public String getAuthor() { - return author; - } - - public Date getDate() { - return date; - } - - public List getEvents() { - return events; - } - - public Vec3d getFinishRecordPos() { - return finish_record_pos; - } - - public Vec3d getStartRecordPos() { - return start_record_pos; - } - - public void setAuthor(String author) { - this.author = author; - } - - public void setDate(Date date) { - this.date = date; - } - - public void setName(String name) { - this.name = name; - } - - public void setEvents(List events) { - this.events = events; - } - - public void setFinishRecordPos(Vec3d finish_record_pos) { - this.finish_record_pos = finish_record_pos; - } - - public void setStartRecordPos(Vec3d start_record_pos) { - this.start_record_pos = start_record_pos; - } - - public void addEvent(RecordEvent event) { - events.add(event); - } - - public RecordEvent getLastEvent(String type) { - for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { - if (r.getType().equals(type)) { - return r; - } - } - return null; - } - - public void save() throws IOException { - StringBuilder text = new StringBuilder(); - - text.append(name).append("\n") - .append(DATE_FORMAT.format(date)).append("\n") - .append(author).append("\n"); - - text.append(start_record_pos.getX()).append("n") - .append(start_record_pos.getY()).append("n") - .append(start_record_pos.getZ()).append("x") - .append(finish_record_pos.getX()).append("n") - .append(finish_record_pos.getY()).append("n") - .append(finish_record_pos.getZ()); - - for (int i = 0; i < events.size(); i++) { - text.append("\n"); - text.append(events.get(i).serialize()); - } - - Files.write(file.toPath(), text.toString().getBytes()); - } - - public static RecordState load(File file) throws Exception { - String text = Files.readString(file.toPath()); - List lines = List.of(text.split("\n")); - - List signature = lines.subList(0,4); - - String name = signature.get(0); - Date date = DATE_FORMAT.parse(signature.get(1)); - String author = signature.get(2); - - String record_pos = signature.get(3); - - String[] lss0 = record_pos.split("x"); - String[] lss1 = lss0[0].split("n"); - String[] lss2 = lss0[1].split("n"); - - Vec3d start_record_pos = new Vec3d( - Float.parseFloat(lss1[0]), - Float.parseFloat(lss1[1]), - Float.parseFloat(lss1[2])); - Vec3d finish_record_pos = new Vec3d( - Float.parseFloat(lss2[0]), - Float.parseFloat(lss2[1]), - Float.parseFloat(lss2[2])); - - List event_lines = lines.subList(4,lines.size()); - List events = event_lines.stream().map(RecordEvent::deserialize).toList(); - - return new RecordState(file, name, date, author, events, start_record_pos, finish_record_pos); - } - - public void remove() { - file.delete(); - Main.me.record_list.removeRecord(this); - Main.me.record_list.getWidget().removeWidget(this); - } -} diff --git a/remappedSrc/themixray/repeating/mod/RenderListener.java b/remappedSrc/themixray/repeating/mod/RenderListener.java deleted file mode 100644 index 9992e48..0000000 --- a/remappedSrc/themixray/repeating/mod/RenderListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package themixray.repeating.mod; - -import net.minecraft.client.gui.DrawContext; - -public interface RenderListener { - default boolean beforeRender() { - return true; - } - - void render(DrawContext context, int mouseX, int mouseY, float delta); -} diff --git a/remappedSrc/themixray/repeating/mod/RepeatingScreen.java b/remappedSrc/themixray/repeating/mod/RepeatingScreen.java deleted file mode 100644 index 19609e4..0000000 --- a/remappedSrc/themixray/repeating/mod/RepeatingScreen.java +++ /dev/null @@ -1,193 +0,0 @@ -package themixray.repeating.mod; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.SliderWidget; -import net.minecraft.text.Text; -import themixray.repeating.mod.widget.RecordListWidget; - -import java.awt.*; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -@Environment(EnvType.CLIENT) -public class RepeatingScreen extends Screen { - private static List render_listeners = new ArrayList<>(); - - public ButtonWidget record_btn; - public ButtonWidget loop_btn; - public ButtonWidget import_btn; - - public SliderWidget pos_delay_slider; - - public boolean was_build = false; - - protected RepeatingScreen() { - super(Text.empty()); - } - - public static void addRenderListener(RenderListener render) { - render_listeners.add(render); - } - - public static void removeRenderListener(RenderListener render) { - render_listeners.remove(render); - } - - public void updateButtons() { - if (was_build) { - record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); - loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); - } - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context,mouseX,mouseY,delta); - - for (RenderListener l : render_listeners) { - if (l.beforeRender()) { - l.render(context, mouseX, mouseY, delta); - } - } - - super.render(context, mouseX, mouseY, delta); - - for (RenderListener l : render_listeners) { - if (!l.beforeRender()) { - l.render(context, mouseX, mouseY, delta); - } - } - } - - @Override - protected void init() { - RecordListWidget list_widget = Main.me.record_list.getWidget(); - - list_widget.method_46421(width / 2 + 2); - list_widget.method_46419(height / 2 - list_widget.getHeight() / 2); - list_widget.init(this); - - - record_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.start_record"), button -> { - if (!Main.me.is_replaying) { - if (Main.me.is_recording) - Main.me.stopRecording(); - else Main.me.startRecording(); - updateButtons(); - } - }) - .dimensions(width / 2 - 120, height / 2 - 32, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) - .build(); - - loop_btn = ButtonWidget.builder(Text.empty(), button -> { - Main.me.loop_replay = !Main.me.loop_replay; - updateButtons(); - }) - .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) - .build(); - - pos_delay_slider = new SliderWidget( - width / 2 - 120, height / 2 + 12, 120, 20, - (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), - (Main.me.record_pos_delay+1d)/101d) { - - @Override - protected void updateMessage() { - double v = value*101d-1d; - if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - } - - @Override - protected void applyValue() { - double v = value*101d-1d; - if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - Main.me.record_pos_delay = (long) v; - Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); - Main.me.conf.save(); - } - - @Override - public void onRelease(double mouseX, double mouseY) { - super.onRelease(mouseX, mouseY); - applyValue(); - } - - @Override - protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { - super.onDrag(mouseX, mouseY, deltaX, deltaY); - applyValue(); - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - updateMessage(); - } - }; - pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); - - import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { - new Thread(() -> { - FileDialog fd = new FileDialog((java.awt.Frame) null); - fd.setMultipleMode(true); - fd.setName("Choose record files"); - fd.setTitle("Choose record files"); - fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); - fd.setVisible(true); - - File[] files = fd.getFiles(); - if (files != null) { - for (File file : files) { - try { - Main.me.record_list.addRecord(file); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }}).start(); - }) - .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) - .build(); - - was_build = true; - - updateButtons(); - - addDrawableChild(loop_btn); - addDrawableChild(record_btn); - addDrawableChild(import_btn); - addDrawableChild(pos_delay_slider); - } - - public T addDrawableChild(T drawableElement) { - return super.addDrawableChild(drawableElement); - } - - public T addDrawable(T drawable) { - return super.addDrawable(drawable); - } - - public T addSelectableChild(T child) { - return super.addSelectableChild(child); - } - - public void remove(Element child) { - super.remove(child); - } -} diff --git a/remappedSrc/themixray/repeating/mod/TickTask.java b/remappedSrc/themixray/repeating/mod/TickTask.java deleted file mode 100644 index f3beed1..0000000 --- a/remappedSrc/themixray/repeating/mod/TickTask.java +++ /dev/null @@ -1,94 +0,0 @@ -package themixray.repeating.mod; - -import java.util.ArrayList; -import java.util.List; - -public abstract class TickTask implements Runnable { - public static List tasks = new ArrayList<>(); - - public static void tickTasks(TickAt at) { - for (TickTask t:new ArrayList<>(tasks)) - if (t.getAt() == at) t.tick(); - } - - private long living; - private long delay; - - private boolean is_repeating; - private long period; - - private boolean is_cancelled; - private TickAt at; - - public enum TickAt { - CLIENT_HEAD, CLIENT_TAIL, - MOVEMENT_HEAD, MOVEMENT_TAIL, - RENDER_HEAD, RENDER_TAIL - } - - public TickTask(long delay, TickAt at) { - this.is_cancelled = false; - this.is_repeating = false; - this.delay = delay; - this.living = 0; - this.period = 0; - this.at = at; - tasks.add(this); - } - - public TickTask(long delay, long period, TickAt at) { - this.is_cancelled = false; - this.is_repeating = true; - this.delay = delay; - this.period = period; - this.living = 0; - this.at = at; - tasks.add(this); - } - - public TickTask(long delay) { - this(delay,TickAt.CLIENT_HEAD); - } - - public TickTask(long delay, long period) { - this(delay,period,TickAt.CLIENT_HEAD); - } - - public void cancel() { - if (!is_cancelled) { - is_cancelled = true; - tasks.remove(this); - } - } - - public boolean isCancelled() { - return is_cancelled; - } - - public TickAt getAt() { - return at; - } - - public void setDelay(long delay) { - if (is_repeating) { - this.delay = delay; - } - } - public long getDelay() { - return this.delay; - } - - public void tick() { - if (living >= delay) { - if (is_repeating) { - delay = period; - run(); - living = -1; - } else { - run(); - cancel(); - } - } - living++; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java deleted file mode 100644 index a39380c..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordBlockBreakEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -package themixray.repeating.mod.event; - -import net.minecraft.util.math.BlockPos; -import themixray.repeating.mod.Main; - -public class RecordBlockBreakEvent extends RecordEvent { - public BlockPos pos; - - public static RecordBlockBreakEvent fromArgs(String[] a) { - return new RecordBlockBreakEvent(new BlockPos( - Integer.parseInt(a[0]), - Integer.parseInt(a[1]), - Integer.parseInt(a[2]))); - } - - public RecordBlockBreakEvent( - BlockPos pos) { - this.pos = pos; - } - - public void replay() { - Main.client.interactionManager.breakBlock(pos); - } - - public String serialize() { - return "b=" + pos.getX() + "&" + pos.getY() + "&" + pos.getZ(); - } - - public String getType() { - return "block_break"; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java deleted file mode 100644 index 59d0b76..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordBlockInteractEvent.java +++ /dev/null @@ -1,46 +0,0 @@ -package themixray.repeating.mod.event; - -import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.Main; - -public class RecordBlockInteractEvent extends RecordEvent { - public Hand hand; - public BlockHitResult hitResult; - - public static RecordBlockInteractEvent fromArgs(String[] a) { - return new RecordBlockInteractEvent( - Hand.valueOf(a[5]), - new BlockHitResult(new Vec3d( - Double.parseDouble(a[0]), - Double.parseDouble(a[1]), - Double.parseDouble(a[2])), - Direction.byId(Integer.parseInt(a[4])), - new BlockPos( - Integer.parseInt(a[0]), - Integer.parseInt(a[1]), - Integer.parseInt(a[2])), - a[3].equals("1"))); - } - - public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { - this.hand = hand; - this.hitResult = hitResult; - } - - public void replay() { - Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult); - } - - public String serialize() { - return "i=" + hitResult.getBlockPos().getX() + "&" + hitResult.getBlockPos().getY() + "&" + hitResult.getBlockPos().getZ() + - "&" + (hitResult.isInsideBlock() ? "1" : "0") + "&" + hitResult.getSide().getId() + "&" + hand.name(); - } - - public String getType() { - return "block_interact"; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java deleted file mode 100644 index 7e4bcca..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordDelayEvent.java +++ /dev/null @@ -1,29 +0,0 @@ -package themixray.repeating.mod.event; - -public class RecordDelayEvent extends RecordEvent { - public long delay; - - public static RecordDelayEvent fromArgs(String[] a) { - return new RecordDelayEvent(Long.parseLong(a[0])); - } - - public RecordDelayEvent(long delay) { - this.delay = delay; - } - - public void replay() { - try { - Thread.sleep(delay / 20 * 1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public String serialize() { - return "d=" + delay; - } - - public String getType() { - return "delay"; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordEvent.java deleted file mode 100644 index e7c2ffe..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package themixray.repeating.mod.event; - -public abstract class RecordEvent { - public abstract void replay(); - public abstract String serialize(); - public abstract String getType(); - - public static RecordEvent deserialize(String t) { - try { - String type = String.valueOf(t.charAt(0)); - String[] args = t.substring(2).split("&"); - if (type.equals("d")) { - return RecordDelayEvent.fromArgs(args); - } else if (type.equals("m")) { - return RecordMoveEvent.fromArgs(args); - } else if (type.equals("p")) { - return RecordInputEvent.fromArgs(args); - } else if (type.equals("b")) { - return RecordBlockBreakEvent.fromArgs(args); - } else if (type.equals("i")) { - return RecordBlockInteractEvent.fromArgs(args); - } - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java deleted file mode 100644 index a760440..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordInputEvent.java +++ /dev/null @@ -1,147 +0,0 @@ -package themixray.repeating.mod.event; - -import themixray.repeating.mod.Main; - -public class RecordInputEvent extends RecordEvent { - public Boolean sneaking; - public Boolean jumping; - public Boolean pressingForward; - public Boolean pressingBack; - public Boolean pressingLeft; - public Boolean pressingRight; - public Boolean sprinting; - - public Float movementSideways; - public Float movementForward; - - public float yaw; - public float head_yaw; - public float body_yaw; - public float pitch; - public float speed; - - public static RecordInputEvent fromArgs(String[] a) { - return new RecordInputEvent( - (a[0].equals("n") ? null : a[0].equals("1")), - (a[1].equals("n") ? null : a[1].equals("1")), - (a[2].equals("n") ? null : Float.parseFloat(a[2])), - (a[3].equals("n") ? null : Float.parseFloat(a[3])), - (a[4].equals("n") ? null : a[4].equals("1")), - (a[5].equals("n") ? null : a[5].equals("1")), - (a[6].equals("n") ? null : a[6].equals("1")), - (a[7].equals("n") ? null : a[7].equals("1")), - Float.parseFloat(a[8]), Float.parseFloat(a[9]), - Float.parseFloat(a[10]), - (a[11].equals("n") ? null : a[11].equals("1")), - Float.parseFloat(a[12]), - Float.parseFloat(a[13])); - } - - public RecordInputEvent(Boolean sneaking, - Boolean jumping, - Float movementSideways, - Float movementForward, - Boolean pressingForward, - Boolean pressingBack, - Boolean pressingLeft, - Boolean pressingRight, - float head_yaw, - float body_yaw, - float head_pitch, - Boolean sprinting, - float yaw, - float speed) { - this.sneaking = sneaking; - this.jumping = jumping; - this.movementSideways = movementSideways; - this.movementForward = movementForward; - this.pressingForward = pressingForward; - this.pressingBack = pressingBack; - this.pressingLeft = pressingLeft; - this.pressingRight = pressingRight; - this.head_yaw = head_yaw; - this.body_yaw = body_yaw; - this.pitch = head_pitch; - this.sprinting = sprinting; - this.yaw = yaw; - this.speed = speed; - } - - public void fillEmpty(RecordInputEvent e) { - if (sneaking == null) sneaking = e.sneaking; - if (jumping == null) jumping = e.jumping; - if (movementSideways == null) movementSideways = e.movementSideways; - if (movementForward == null) movementForward = e.movementForward; - if (pressingForward == null) pressingForward = e.pressingForward; - if (pressingBack == null) pressingBack = e.pressingBack; - if (pressingLeft == null) pressingLeft = e.pressingLeft; - if (pressingRight == null) pressingRight = e.pressingRight; - if (sprinting == null) sprinting = e.sprinting; - } - - public boolean isEmpty() { - return sneaking == null && - jumping == null && - movementSideways == null && - movementForward == null && - pressingForward == null && - pressingBack == null && - pressingLeft == null && - pressingRight == null && - sprinting == null; - } - - public void replay() { - Main.input_replay = this; - } - - public void inputCallback() { - if (sprinting != null && Main.client.player.isSprinting() != sprinting) - Main.client.player.setSprinting(sprinting); - if (Main.client.player.getYaw() != yaw) - Main.client.player.setYaw(yaw); - if (Main.client.player.getHeadYaw() != head_yaw) - Main.client.player.setHeadYaw(head_yaw); - if (Main.client.player.getBodyYaw() != body_yaw) - Main.client.player.setBodyYaw(body_yaw); - if (Main.client.player.getPitch() != pitch) - Main.client.player.setPitch(pitch); - if (Main.client.player.getMovementSpeed() != speed) - Main.client.player.setMovementSpeed(speed); - if (sneaking != null && Main.client.player.input.sneaking != sneaking) - Main.client.player.input.sneaking = sneaking; - if (jumping != null && Main.client.player.input.jumping != jumping) - Main.client.player.input.jumping = jumping; - if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) - Main.client.player.input.movementSideways = movementSideways; - if (movementForward != null && Main.client.player.input.movementForward != movementForward) - Main.client.player.input.movementForward = movementForward; - if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) - Main.client.player.input.pressingForward = pressingForward; - if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) - Main.client.player.input.pressingBack = pressingBack; - if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) - Main.client.player.input.pressingLeft = pressingLeft; - if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) - Main.client.player.input.pressingRight = pressingRight; - } - - public String serialize() { - return "p=" + - ((sneaking == null) ? "n" : (sneaking ? "1" : "0")) + "&" + - ((jumping == null) ? "n" : (jumping ? "1" : "0")) + "&" + - ((movementSideways == null) ? "n" : movementSideways) + "&" + - ((movementForward == null) ? "n" : movementForward) + "&" + - ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")) + "&" + - ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")) + "&" + - ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")) + "&" + - ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")) + "&" + - head_yaw + "&" + body_yaw + "&" + pitch + "&" + - ((sprinting == null) ? "n" : (sprinting ? "1" : "0") + - "&" + yaw + "&" + speed); - } - - public String getType() { - return "input"; - } -} diff --git a/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java b/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java deleted file mode 100644 index 7b964de..0000000 --- a/remappedSrc/themixray/repeating/mod/event/RecordMoveEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -package themixray.repeating.mod.event; - -import net.minecraft.entity.MovementType; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.Main; - -public class RecordMoveEvent extends RecordEvent { - public Vec3d vec; - public float yaw; - public float pitch; - - public static RecordMoveEvent fromArgs(String[] a) { - return new RecordMoveEvent(new Vec3d( - Double.parseDouble(a[0]), - Double.parseDouble(a[1]), - Double.parseDouble(a[2])), - Float.parseFloat(a[3]), - Float.parseFloat(a[4])); - } - - public RecordMoveEvent(Vec3d vec, float yaw, float pitch) { - this.vec = vec; - this.yaw = yaw; - this.pitch = pitch; - } - - public void replay() { - Vec3d p = Main.client.player.getPos(); - Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); - Main.client.player.move(MovementType.SELF, v); - Main.client.player.setYaw(yaw); - Main.client.player.setPitch(pitch); - } - - public String serialize() { - return "m=" + vec.getX() + "&" + vec.getY() + "&" + vec.getZ() + "&" + yaw + "&" + pitch; - } - - public String getType() { - return "move"; - } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java b/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java deleted file mode 100644 index 60e9e64..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/ClientMixin.java +++ /dev/null @@ -1,24 +0,0 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.MinecraftClient; -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.Main; -import themixray.repeating.mod.TickTask; - -@Mixin(MinecraftClient.class) -public abstract class ClientMixin { - @Inject(at = @At(value = "HEAD"), method = "tick") - private void onTickHead(CallbackInfo ci) { - if (Main.me.is_recording) - Main.me.recordAllInput(); - TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); - } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java b/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java deleted file mode 100644 index eed7b58..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/EntityMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.entity.Entity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import themixray.repeating.mod.Main; - -import java.util.UUID; - -@Mixin(Entity.class) -public abstract class EntityMixin { - @Shadow public abstract UUID getUuid(); - - @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) - private void onSprint(boolean sprinting,CallbackInfo ci) { - if (Main.client.player != null) { - if (getUuid().equals(Main.client.player.getUuid())) { - if (Main.me.is_replaying) { - if (Main.input_replay != null && - Main.input_replay.sprinting != null && - Main.input_replay.sprinting != sprinting) { - ci.cancel(); - } - } - } - } - } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java b/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java deleted file mode 100644 index 800e147..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/InputMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -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.Main; - -@Mixin(KeyboardInput.class) -public abstract class InputMixin { - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { - if (Main.me.is_replaying) { - if (Main.input_replay != null) { - Main.input_replay.inputCallback(); - } - } - } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java b/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java deleted file mode 100644 index 45190ba..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/MovementMixin.java +++ /dev/null @@ -1,44 +0,0 @@ -package themixray.repeating.mod.mixin; - -import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.hit.HitResult; -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.Main; -import themixray.repeating.mod.event.RecordBlockBreakEvent; -import themixray.repeating.mod.event.RecordBlockInteractEvent; -import themixray.repeating.mod.TickTask; - -@Mixin(ClientPlayerEntity.class) -public abstract class MovementMixin { - - @Inject(at = @At(value = "HEAD"), method = "init") - private void init(CallbackInfo ci) { - PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { - if (Main.me.is_recording) - Main.me.recordTick(new RecordBlockBreakEvent(pos)); - }); - - UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { - if (hitResult.getType().equals(HitResult.Type.BLOCK)) - if (Main.me.is_recording) - Main.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); - return ActionResult.PASS; - }); - } - - @Inject(at = @At(value = "HEAD"), method = "tickMovement") - private void onMoveHead(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onMoveTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); - } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java b/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java deleted file mode 100644 index 4f85878..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/NetworkMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.listener.ServerPlayPacketListener; -import net.minecraft.network.packet.Packet; -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 java.time.Duration; -import java.util.function.BooleanSupplier; - -@Mixin(ClientPlayNetworkHandler.class) -public abstract class NetworkMixin { -// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") -// private void onSendPacket1Head(Packet packet, -// CallbackInfo ci) { -// -// } -// -// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") -// private void onSendPacket2Head(Packet packet, -// BooleanSupplier sendCondition, -// Duration expirationTime, -// CallbackInfo ci) { -// -// } -} diff --git a/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java b/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java deleted file mode 100644 index edb363d..0000000 --- a/remappedSrc/themixray/repeating/mod/mixin/RendererMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.render.*; -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.TickTask; - -@Mixin(GameRenderer.class) -public abstract class RendererMixin { - @Inject(at = @At(value = "HEAD"), method = "tick") - private void onTickHead(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/RenderHelper.java b/remappedSrc/themixray/repeating/mod/render/RenderHelper.java deleted file mode 100644 index 4f80464..0000000 --- a/remappedSrc/themixray/repeating/mod/render/RenderHelper.java +++ /dev/null @@ -1,200 +0,0 @@ -package themixray.repeating.mod.render; - -import lombok.experimental.UtilityClass; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.render.buffer.WorldBuffer; -import themixray.repeating.mod.render.shader.ShaderManager; - -import java.awt.*; - -import static org.lwjgl.opengl.GL33.*; - -@UtilityClass -public class RenderHelper { - public WorldBuffer startLines(WorldRenderContext context) { - glEnable(GL_LINE_SMOOTH); - return new WorldBuffer(GL_LINES, ShaderManager.getPositionColorShader(), context); - } - - public void endLines(WorldBuffer buffer) { - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(false); - buffer.draw(); - glDepthMask(true); - glDisable(GL_BLEND); - } - - public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) { - buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - } - - public static WorldBuffer startTri(WorldRenderContext context) { - return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context); - } - - public static void endTri(WorldBuffer buffer) { - //glDepthRange(0, 0.7); - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDepthMask(false); - buffer.draw(); - glDepthMask(true); - glEnable(GL_CULL_FACE); - glDisable(GL_BLEND); - glDepthRange(0, 1); - } - - public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) { - buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - } - - public static void drawRectFromTri(WorldBuffer buffer, - float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - Color color) { - drawTri(buffer, - x1, y1, z1, - x2, y2, z2, - x3, y3, z3, - color); - drawTri(buffer, - x3, y3, z3, - x4, y4, z4, - x1, y1, z1, - color); - } - - public void drawRectFromLines(WorldBuffer buffer, - float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4, - Color color) { - drawLine(buffer, - x1, y1, z1, - x2, y2, z2, - color); - drawLine(buffer, - x2, y2, z2, - x3, y3, z3, - color); - drawLine(buffer, - x3, y3, z3, - x4, y4, z4, - color); - drawLine(buffer, - x4, y4, z4, - x1, y1, z1, - color); - } - - public void drawBoxFromTri(WorldBuffer buffer, - float x1, float y1, float z1, - float x2, float y2, float z2, - Color color) { - float[][] v = new float[][]{ - new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, - new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; - - drawRectFromTri(buffer, - v[0][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[0][2], - v[1][0], v[1][1], v[0][2], - v[0][0], v[1][1], v[0][2], - color); - - drawRectFromTri(buffer, - v[0][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[1][2], - v[0][0], v[0][1], v[1][2], - color); - - drawRectFromTri(buffer, - v[0][0], v[0][1], v[0][2], - v[0][0], v[0][1], v[1][2], - v[0][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[0][2], - color); - - drawRectFromTri(buffer, - v[0][0], v[0][1], v[1][2], - v[1][0], v[0][1], v[1][2], - v[1][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[1][2], - color); - - drawRectFromTri(buffer, - v[0][0], v[1][1], v[0][2], - v[1][0], v[1][1], v[0][2], - v[1][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[1][2], - color); - - drawRectFromTri(buffer, - v[1][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[1][2], - v[1][0], v[1][1], v[1][2], - v[1][0], v[1][1], v[0][2], - color); - } - - public void drawBoxFromLines(WorldBuffer buffer, - float x1, float y1, float z1, - float x2, float y2, float z2, - Color color) { - float[][] v = new float[][]{ - new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)}, - new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}}; - - drawRectFromLines(buffer, - v[0][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[0][2], - v[1][0], v[1][1], v[0][2], - v[0][0], v[1][1], v[0][2], - color); - - drawRectFromLines(buffer, - v[0][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[1][2], - v[0][0], v[0][1], v[1][2], - color); - - drawRectFromLines(buffer, - v[0][0], v[0][1], v[0][2], - v[0][0], v[0][1], v[1][2], - v[0][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[0][2], - color); - - drawRectFromLines(buffer, - v[0][0], v[0][1], v[1][2], - v[1][0], v[0][1], v[1][2], - v[1][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[1][2], - color); - - drawRectFromLines(buffer, - v[0][0], v[1][1], v[0][2], - v[1][0], v[1][1], v[0][2], - v[1][0], v[1][1], v[1][2], - v[0][0], v[1][1], v[1][2], - color); - - drawRectFromLines(buffer, - v[1][0], v[0][1], v[0][2], - v[1][0], v[0][1], v[1][2], - v[1][0], v[1][1], v[1][2], - v[1][0], v[1][1], v[0][2], - color); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/RenderSystem.java b/remappedSrc/themixray/repeating/mod/render/RenderSystem.java deleted file mode 100644 index a5fa15c..0000000 --- a/remappedSrc/themixray/repeating/mod/render/RenderSystem.java +++ /dev/null @@ -1,13 +0,0 @@ -package themixray.repeating.mod.render; - -import lombok.experimental.UtilityClass; -import themixray.repeating.mod.render.buffer.BufferManager; -import themixray.repeating.mod.render.shader.ShaderManager; - -@UtilityClass -public class RenderSystem { - public static void init() { - BufferManager.init(); - ShaderManager.init(); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java b/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java deleted file mode 100644 index 76d2853..0000000 --- a/remappedSrc/themixray/repeating/mod/render/buffer/BufferManager.java +++ /dev/null @@ -1,48 +0,0 @@ -package themixray.repeating.mod.render.buffer; - -import lombok.experimental.UtilityClass; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; - -import java.nio.FloatBuffer; - -import static org.lwjgl.opengl.GL33.*; - -@UtilityClass -public class BufferManager { - private int vao; - private int vbo; - - private int prevVao; - - public void init() { - ClientLifecycleEvents.CLIENT_STARTED.register(client -> { - vao = glGenVertexArrays(); - vbo = glGenBuffers(); - }); - } - - public static void bindBuffer() { - glBindBuffer(GL_ARRAY_BUFFER, vbo); - } - - public static void unbindBuffer() { - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - public static void writeBuffer(FloatBuffer buffer) { - glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); - } - - public static void draw(int drawMode, int verts) { - glDrawArrays(drawMode, 0, verts); - } - - public static void bind() { - prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING); - glBindVertexArray(vao); - } - - public static void unbind() { - glBindVertexArray(prevVao); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java b/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java deleted file mode 100644 index 5aaa113..0000000 --- a/remappedSrc/themixray/repeating/mod/render/buffer/Vertex.java +++ /dev/null @@ -1,30 +0,0 @@ -package themixray.repeating.mod.render.buffer; - -import lombok.Getter; - -public class Vertex { - @Getter - private float x; - @Getter - private float y; - @Getter - private float z; - @Getter - private float r; - @Getter - private float g; - @Getter - private float b; - @Getter - private float a; - - public Vertex(float x, float y, float z, float r, float g, float b, float a) { - this.x = x; - this.y = y; - this.z = z; - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } -} \ No newline at end of file diff --git a/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java b/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java deleted file mode 100644 index 390f97d..0000000 --- a/remappedSrc/themixray/repeating/mod/render/buffer/WorldBuffer.java +++ /dev/null @@ -1,82 +0,0 @@ -package themixray.repeating.mod.render.buffer; - -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.util.math.Vec3d; -import org.apache.commons.lang3.ArrayUtils; -import org.joml.Matrix4f; -import org.lwjgl.BufferUtils; -import themixray.repeating.mod.render.shader.Shader; - -import java.nio.FloatBuffer; -import java.util.ArrayList; -import java.util.List; - -import static org.lwjgl.opengl.GL33.*; - -public class WorldBuffer { - private final List vertices = new ArrayList<>(); - private final int drawMode; - private final Shader shader; - private FloatBuffer projectionMatrix; - private final Vec3d cameraPos; - - public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) { - this.drawMode = drawMode; - this.shader = shader; - this.cameraPos = worldRenderContext.camera().getPos(); - makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix()); - } - - public void vert(float x, float y, float z, float r, float g, float b, float a) { - vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a)); - } - - public void draw() { - BufferManager.bind(); - BufferManager.bindBuffer(); - - BufferManager.writeBuffer(getBuffer()); - applyProjectionMatrix(); - - glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, vertices.size() * 3 * 4L); - glEnableVertexAttribArray(1); - - BufferManager.unbindBuffer(); - - shader.bind(); - BufferManager.draw(drawMode, this.vertices.size()); - shader.unbind(); - - BufferManager.unbind(); - } - - private FloatBuffer getBuffer() { - FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7); - ArrayList floats = new ArrayList<>(); - for (Vertex vertex : vertices) { - floats.add(vertex.getX()); - floats.add(vertex.getY()); - floats.add(vertex.getZ()); - } - for (Vertex vertex : vertices) { - floats.add(vertex.getR()); - floats.add(vertex.getG()); - floats.add(vertex.getB()); - floats.add(vertex.getA()); - } - Float[] floatArray = new Float[floats.size()]; - floats.toArray(floatArray); - floatBuffer.put(ArrayUtils.toPrimitive(floatArray)); - return floatBuffer.flip(); - } - - private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) { - this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16)); - } - - private void applyProjectionMatrix() { - shader.uniformMatrix4f("u_projection", projectionMatrix); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/shader/Shader.java b/remappedSrc/themixray/repeating/mod/render/shader/Shader.java deleted file mode 100644 index e11dd9a..0000000 --- a/remappedSrc/themixray/repeating/mod/render/shader/Shader.java +++ /dev/null @@ -1,42 +0,0 @@ -package themixray.repeating.mod.render.shader; - -import lombok.Getter; - -import java.nio.FloatBuffer; - -import static org.lwjgl.opengl.GL33.*; - -public class Shader { - @Getter - private final int id; - - - public Shader(String name) { - int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX); - int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT); - this.id = glCreateProgram(); - glAttachShader(id, v); - glAttachShader(id, f); - glLinkProgram(id); - } - - public void bind() { - glUseProgram(id); - } - - public void unbind() { - glUseProgram(0); - } - - public void uniformMatrix4f(String name, FloatBuffer matrix) { - bind(); - glUniformMatrix4fv(glGetUniformLocation(id, name), false, matrix); - unbind(); - } - - public void uniformValue2f(String name, float value1, float value2) { - bind(); - glUniform2f(glGetUniformLocation(id, name), value1, value2); - unbind(); - } -} diff --git a/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java b/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java deleted file mode 100644 index f11c98b..0000000 --- a/remappedSrc/themixray/repeating/mod/render/shader/ShaderManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package themixray.repeating.mod.render.shader; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.platform.TextureUtil; -import lombok.Getter; -import lombok.SneakyThrows; -import lombok.experimental.UtilityClass; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.GlImportProcessor; -import net.minecraft.resource.Resource; -import net.minecraft.resource.ResourceFactory; -import net.minecraft.util.Identifier; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Optional; - -import static org.lwjgl.opengl.GL33.*; - -@UtilityClass -public class ShaderManager { - @Getter - private Shader positionColorShader; - - public void init() { - ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders()); - } - - private void loadShaders() { - positionColorShader = new Shader("position_color"); - } - - public int loadShaderProgram(String name, ShaderType type) { - try { - boolean file_present = true; - ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager(); - Optional resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension)); - int i = glCreateShader(type.glType); - if (resource.isPresent()) { - GlStateManager.glShaderSource(i, new GlImportProcessor() { - @SneakyThrows - @Nullable - @Override - public String loadImport(boolean inline, String name) { - return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8); - } - }.readSource(readResourceAsString(resource.get().getInputStream()))); - } else file_present = false; - glCompileShader(i); - if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) { - String shaderInfo = StringUtils.trim(glGetShaderInfoLog(i, 32768)); - throw new IOException("Couldn't compile " + type.name + " program (" + name + ") : " + shaderInfo); - } - return i; - } catch (IOException e) { - e.printStackTrace(); - } - return 0; - } - - private String readResourceAsString(InputStream inputStream) { - ByteBuffer byteBuffer = null; - try { - byteBuffer = TextureUtil.readResource(inputStream); - int i = byteBuffer.position(); - byteBuffer.rewind(); - return MemoryUtil.memASCII(byteBuffer, i); - } catch (IOException ignored) { - } finally { - if (byteBuffer != null) { - MemoryUtil.memFree(byteBuffer); - } - } - return null; - } - - public enum ShaderType { - VERTEX("vertex", ".vsh", GL_VERTEX_SHADER), - FRAGMENT("fragment", ".fsh", GL_FRAGMENT_SHADER); - private final String name; - private final String fileExtension; - private final int glType; - - ShaderType(String name, String fileExtension, int glType) { - this.name = name; - this.fileExtension = fileExtension; - this.glType = glType; - } - } -} diff --git a/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java b/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java deleted file mode 100644 index 1ac42b2..0000000 --- a/remappedSrc/themixray/repeating/mod/widget/RecordListWidget.java +++ /dev/null @@ -1,150 +0,0 @@ -package themixray.repeating.mod.widget; - -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.widget.*; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.text.Text; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.RecordState; -import themixray.repeating.mod.RepeatingScreen; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -public class RecordListWidget extends ScrollableWidget { - private List widgets = new ArrayList<>(); - private boolean focused = false; - - public RecordListWidget(int x, int y, int width, int height) { - super(x,y,width,height,Text.empty()); - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - focused = true; - boolean res = super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - focused = false; - return res; - } - - @Override - protected double getDeltaYPerScroll() { - return 10; - } - - @Override - protected void renderContents(DrawContext ctx, int mouseX, int mouseY, float delta) { - int y = 0; - for (RecordWidget wid: widgets) { - wid.method_46419(y); - wid.render(ctx, mouseX, (int) (mouseY + this.getScrollY()), delta); - - y += wid.getHeight(); - y += 2; - } - } - - public void addWidget(RecordState record) { - RecordWidget widget = new RecordWidget(0, 0, width, 55, record, this); - widget.init(null); - widgets.add(0, widget); - } - - public void removeWidget(RecordState record) { - widgets.removeIf(i -> i.getRecord().equals(record)); - } - - @Override - public void setFocused(boolean focused) { - - } - - @Override - public boolean isFocused() { - return focused; - } - - @Override - protected int getContentsHeight() { - return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0; - } - - public void init(RepeatingScreen screen) { - for (RecordWidget widget : widgets) { - widget.init(screen); - } - - screen.addDrawableChild(this); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - - } - - public RecordWidget getWidget(RecordState record) { - for (RecordWidget widget : widgets) { - if (widget.getRecord().equals(record)) { - return widget; - } - } - return null; - } - - public interface transport { - boolean check(ClickableWidget ch); - } - - public boolean checkTransport(transport tr) { - for (RecordWidget wid : widgets) { - for (ClickableWidget child : wid.getChildren()) { - if (tr.check(child)) { - return true; - } - } - } - return false; - } - - public boolean checkTransportNF(transport tr) { - for (RecordWidget wid : widgets) { - for (ClickableWidget child : wid.getChildren()) { - boolean res = tr.check(child); - - if (res) { - child.setFocused(true); - return true; - } else { - child.setFocused(false); - } - } - } - return false; - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - return checkTransportNF((c) -> c.mouseClicked(mouseX, mouseY + this.getScrollY(), button)) || super.mouseClicked(mouseX, mouseY, button); - } - - @Override - public boolean charTyped(char chr, int modifiers) { - return checkTransport((c) -> c.charTyped(chr, modifiers)) || super.charTyped(chr, modifiers); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - return checkTransport((c) -> c.keyPressed(keyCode, scanCode, modifiers)) || super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean keyReleased(int keyCode, int scanCode, int modifiers) { - return checkTransport((c) -> c.keyReleased(keyCode, scanCode, modifiers)) || super.keyReleased(keyCode, scanCode, modifiers); - } -} diff --git a/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java b/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java deleted file mode 100644 index bef439a..0000000 --- a/remappedSrc/themixray/repeating/mod/widget/RecordWidget.java +++ /dev/null @@ -1,196 +0,0 @@ -package themixray.repeating.mod.widget; - -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.widget.*; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.RecordState; -import themixray.repeating.mod.RenderListener; -import themixray.repeating.mod.RepeatingScreen; - -import java.awt.*; -import java.io.IOException; -import java.util.ArrayList; - -import java.util.List; -import java.util.function.Consumer; - -public class RecordWidget implements Drawable, Widget { - private RecordState record; - - private List children; - - private RecordListWidget parent; - - private int x; - private int y; - private int width; - private int height; - - public RecordWidget(int x, int y, int width, int height, RecordState record, RecordListWidget parent) { - this.parent = parent; - this.record = record; - - this.x = x; - this.y = y; - this.width = width; - this.height = height; - - this.children = new ArrayList<>(); - } - - public void method_46421(int x) { - this.x = x; - } - public void method_46419(int y) { - this.y = y; - } - public int getX() { - return x; - } - public int getY() { - return y; - } - public int getWidth() { - return width; - } - public int getHeight() { - return height; - } - - public List getChildren() { - return children; - } - - @Override - public void forEachChild(Consumer consumer) { - children.forEach(consumer); - } - - public void init(RepeatingScreen screen) { - this.children = new ArrayList<>(); - - TextFieldWidget name_widget = new TextFieldWidget( - Main.client.textRenderer, - parent.getX() + getX() + 5, - parent.getY() + getY() + 5, - 102, - 10, - Text.empty()); - - name_widget.setText(record.getName()); - - name_widget.setChangedListener((s) -> { - record.setName(s); - try { - record.save(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - - children.add(name_widget); - - ButtonWidget delete_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.delete"), (i) -> { - record.remove(); - }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4, 65, 13).build(); - - children.add(delete_button); - - ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> { - if (Desktop.isDesktopSupported()) { - Desktop desk = Desktop.getDesktop(); - try { - desk.browseFileDirectory(record.getFile()); - } catch (Exception e) { - try { - desk.browse(record.getFile().toURI()); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - } - }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build(); - - children.add(export_button); - - ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.start"), (i) -> { - if (Main.me.is_replaying) { - Main.me.stopReplay(); - } - - i.setMessage(Text.translatable("text.repeating-mod.stop")); - Main.me.now_record = record; - Main.me.startReplay(); - }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); - - children.add(replay_button); - } - - public RecordState getRecord() { - return record; - } - - public void drawText(int x, int y, DrawContext ctx, List lines, float size, int line_height, boolean shadow) { - ctx.getMatrices().push(); - ctx.getMatrices().scale(size, size, size); - - int now_y = y; - - for (Text line : lines) { - ctx.drawText(Main.client.textRenderer, line, (int) (x / size), (int) (now_y / size), line.getStyle().getColor().getRgb(), shadow); - now_y += line_height; - } - - ctx.getMatrices().pop(); - } - - @Override - public void render(DrawContext ctx, int mouseX, int mouseY, float delta) { - int color = record.equals(Main.me.now_record) ? 0xFF555555 : 0xFF333333; - - ctx.fill(parent.getX() + getX(), - parent.getY() + getY(), - parent.getX() + getX() + getWidth(), - parent.getY() + getY() + getHeight(), - color); - - drawText( - parent.getX() + getX() + 5, - parent.getY() + getY() + 5 + 12, - ctx, List.of( - Text.translatable("text.repeating-mod.recorded_at") - .append(": ") - .styled((s) -> s.withColor(0xbbbbbb)), - Text.literal(RecordState.DATE_FORMAT.format(record.getDate())).styled((s) -> s.withColor(0xffffff)), - Text.translatable("text.repeating-mod.author") - .append(": ") - .styled((s) -> s.withColor(0xbbbbbb)), - Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) - ), 1, - 9, - false); - - if (!children.isEmpty()) { - ClickableWidget name_widget = children.get(0); - name_widget.setPosition(parent.getX() + getX() + 5, parent.getY() + getY() + 5); - - ClickableWidget delete_button = children.get(1); - delete_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4); - - ClickableWidget export_button = children.get(2); - export_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14); - - ClickableWidget replay_button = children.get(3); - replay_button.setPosition(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28); - } - - for (ClickableWidget child : children) { - child.render(ctx, mouseX, mouseY, delta); - } - } -} diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/themixray/repeating/mod/EasyConfig.java index 8836722..0ee871c 100644 --- a/src/main/java/themixray/repeating/mod/EasyConfig.java +++ b/src/main/java/themixray/repeating/mod/EasyConfig.java @@ -1,103 +1,103 @@ -package themixray.repeating.mod; - -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(File f, Map def) { - this.path = f.toPath(); - this.file = f; - this.data = new HashMap<>(); - - if (!file.exists()) { - try { - file.createNewFile(); - write(def); - } catch (IOException e) { - e.printStackTrace(); - } - } - - reload(); - - for (Map.Entry m:def.entrySet()) - if (!data.containsKey(m.getKey())) - data.put(m.getKey(),m.getValue()); - - save(); - } - public EasyConfig(Path f, Map def) { - this(f.toFile(),def); - } - public EasyConfig(String parent,String child,Map def) { - this(new File(parent,child),def); - } - public EasyConfig(File parent,String child,Map def) { - this(new File(parent,child),def); - } - public EasyConfig(Path parent,String child,Map def) { - this(new File(parent.toFile(),child),def); - } - - public EasyConfig(File f) { - this(f,new HashMap<>()); - } - public EasyConfig(Path path) { - this(path.toFile(),new HashMap<>()); - } - public EasyConfig(String parent,String child) { - this(new File(parent,child),new HashMap<>()); - } - public EasyConfig(File parent,String child) { - this(new File(parent,child),new HashMap<>()); - } - public EasyConfig(Path parent,String child) { - this(new File(parent.toFile(),child),new HashMap<>()); - } - - public void reload() { - data = read(); - } - public void save() { - write(data); - } - - private String toText(Map p) { - StringBuilder t = new StringBuilder(); - for (Map.Entry e:p.entrySet()) - t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); - return t.toString(); - } - private Map toMap(String j) { - Map m = new HashMap<>(); - for (String l:j.split("\n")) { - String s[] = l.split("="); - m.put(s[0],s[1]); - } - return m; - } - - 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, toText(p).getBytes()); - } catch (IOException e) { - e.printStackTrace(); - } - } -} +package themixray.repeating.mod; + +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(File f, Map def) { + this.path = f.toPath(); + this.file = f; + this.data = new HashMap<>(); + + if (!file.exists()) { + try { + file.createNewFile(); + write(def); + } catch (IOException e) { + e.printStackTrace(); + } + } + + reload(); + + for (Map.Entry m:def.entrySet()) + if (!data.containsKey(m.getKey())) + data.put(m.getKey(),m.getValue()); + + save(); + } + public EasyConfig(Path f, Map def) { + this(f.toFile(),def); + } + public EasyConfig(String parent,String child,Map def) { + this(new File(parent,child),def); + } + public EasyConfig(File parent,String child,Map def) { + this(new File(parent,child),def); + } + public EasyConfig(Path parent,String child,Map def) { + this(new File(parent.toFile(),child),def); + } + + public EasyConfig(File f) { + this(f,new HashMap<>()); + } + public EasyConfig(Path path) { + this(path.toFile(),new HashMap<>()); + } + public EasyConfig(String parent,String child) { + this(new File(parent,child),new HashMap<>()); + } + public EasyConfig(File parent,String child) { + this(new File(parent,child),new HashMap<>()); + } + public EasyConfig(Path parent,String child) { + this(new File(parent.toFile(),child),new HashMap<>()); + } + + public void reload() { + data = read(); + } + public void save() { + write(data); + } + + private String toText(Map p) { + StringBuilder t = new StringBuilder(); + for (Map.Entry e:p.entrySet()) + t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); + return t.toString(); + } + private Map toMap(String j) { + Map m = new HashMap<>(); + for (String l:j.split("\n")) { + String s[] = l.split("="); + m.put(s[0],s[1]); + } + return m; + } + + 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, toText(p).getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/themixray/repeating/mod/Main.java index c2b8645..f213ee2 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/themixray/repeating/mod/Main.java @@ -1,339 +1,341 @@ -package themixray.repeating.mod; - -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.option.KeyBinding; -import net.minecraft.client.util.InputUtil; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.Vec3d; -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import themixray.repeating.mod.event.events.DelayEvent; -import themixray.repeating.mod.event.RecordEvent; -import themixray.repeating.mod.event.events.InputEvent; -import themixray.repeating.mod.event.events.MoveEvent; -import themixray.repeating.mod.render.RenderHelper; -import themixray.repeating.mod.render.RenderSystem; -import themixray.repeating.mod.render.buffer.WorldBuffer; - -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.List; - -public class Main implements ClientModInitializer { - public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); - public static final MinecraftClient client = MinecraftClient.getInstance(); - public static final FabricLoader loader = FabricLoader.getInstance(); - public static Main me; - - public RecordList record_list; - public RecordState now_record; - - public boolean is_recording = false; - public long last_record = -1; - public TickTask move_tick = null; - - public TickTask replay_tick = null; - public boolean is_replaying = false; - public boolean loop_replay = false; - public static InputEvent input_replay = null; - - public long living_ticks = 0; - - public static RepeatingScreen menu; - private static KeyBinding menu_key; - private static KeyBinding toggle_replay_key; - private static KeyBinding toggle_record_key; - - public long record_pos_delay = 20; - - public static Random rand = new Random(); - - public EasyConfig conf; - public File records_folder; - - @Override - public void onInitializeClient() { - LOGGER.info("Repeating mod initialized"); - me = this; - - now_record = null; - - records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); - if (!records_folder.exists()) records_folder.mkdir(); - - record_list = new RecordList(records_folder); - record_list.loadRecords(); - - RenderSystem.init(); - WorldRenderEvents.LAST.register(context -> { - WorldBuffer buffer = RenderHelper.startTri(context); - if (now_record != null) { - Vec3d start_pos = now_record.getStartRecordPos(); - Vec3d finish_pos = now_record.getFinishRecordPos(); - - if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128)); - if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128)); - } - RenderHelper.endTri(buffer); - }); - - ClientTickEvents.END_CLIENT_TICK.register(client -> { - TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); - }); - - Map def = new HashMap<>(); - def.put("record_pos_delay", String.valueOf(record_pos_delay)); - - conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); - - record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); - - menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.menu",InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_J,"text.repeating-mod.name")); - toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, - -1,"text.repeating-mod.name")); - toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, - -1,"text.repeating-mod.name")); - - menu = new RepeatingScreen(); - ClientTickEvents.END_CLIENT_TICK.register(client -> { - if (menu_key.wasPressed()) - client.setScreen(menu); - if (toggle_replay_key.wasPressed()) { - if (now_record != null) { - if (!is_recording) { - if (is_replaying) - stopReplay(); - else startReplay(); - menu.updateButtons(); - } - } - } - if (toggle_record_key.wasPressed()) { - if (!is_replaying) { - if (is_recording) - stopRecording(); - else startRecording(); - menu.updateButtons(); - } - } - }); - - new TickTask(0,0) { - @Override - public void run() { - living_ticks++; - } - }; - - System.setProperty("java.awt.headless", "false"); - } - - public void setNowRecord(RecordState record) { - now_record = record; - } - - public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { - RenderHelper.drawRectFromTri(buffer, - (float) pos.getX() - 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() - 0.25F, - - (float) pos.getX() + 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() - 0.25F, - - (float) pos.getX() + 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() + 0.25F, - - (float) pos.getX() - 0.25F, - (float) pos.getY() + 0.01F, - (float) pos.getZ() + 0.25F, - color); - } - - public void startRecording() { - is_recording = true; - menu.updateButtons(); - - now_record = record_list.newRecord(); - - Vec3d start_pos = client.player.getPos(); - now_record.addEvent(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); - now_record.setStartRecordPos(start_pos); - - if (record_pos_delay > 0) { - move_tick = new TickTask( - record_pos_delay, - record_pos_delay) { - @Override - public void run() { - now_record.addEvent(new MoveEvent(client.player.getPos(), - client.player.getHeadYaw(), client.player.getPitch())); - } - }; - } - - sendMessage(Text.translatable("message.repeating-mod.record_start")); - } - - public void recordTick(RecordEvent e) { - if (is_recording) { - long now = living_ticks; - if (last_record != -1) { - long diff = now - last_record - 2; - if (diff > 0) now_record.addEvent(new DelayEvent(diff)); - } - now_record.addEvent(e); - last_record = now; - } - } - - public void recordAllInput() { - if (client.player == null) { - stopRecording(); - return; - } - - InputEvent l = ((InputEvent) now_record.getLastEvent("input")); - if (l == null) { - InputEvent e = new InputEvent( - client.player.input.sneaking, - client.player.input.jumping, - client.player.input.movementSideways, - client.player.input.movementForward, - client.player.input.pressingForward, - client.player.input.pressingBack, - client.player.input.pressingLeft, - client.player.input.pressingRight, - client.player.getHeadYaw(), - client.player.getBodyYaw(), - client.player.getPitch(), - client.player.isSprinting(), - client.player.getYaw(), - client.player.getMovementSpeed()); - recordTick(e); - } else { - InputEvent e = new InputEvent( - ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, - ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, - (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, - (((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, - ((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, - ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, - ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, - ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, - client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), - ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), - client.player.getYaw(),client.player.getMovementSpeed()); - - if (!(e.isEmpty() && - e.yaw == l.yaw && - e.head_yaw == l.head_yaw && - e.pitch == l.pitch && - e.body_yaw == l.body_yaw)) { - e.fillEmpty(l); - recordTick(e); - } - } - } - - public void stopRecording() { - is_recording = false; - now_record.setFinishRecordPos(client.player.getPos()); - try { - now_record.save(); - } catch (IOException e) { - throw new RuntimeException(e); - } - if (move_tick != null) { - move_tick.cancel(); - move_tick = null; - } - menu.updateButtons(); - last_record = -1; - sendMessage(Text.translatable("message.repeating-mod.record_stop")); - } - - - public void startReplay() { - is_recording = false; - is_replaying = true; - menu.updateButtons(); - - List events = now_record.getEvents(); - - replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { - public int replay_index = 0; - - @Override - public void run() { - if (!is_replaying) { - cancel(); - return; - } - - RecordEvent e = events.get(replay_index); - if (e instanceof DelayEvent) { - setDelay(((DelayEvent) e).delay); - } else { - e.replay(); - } - - replay_index++; - if (!loop_replay) { - if (replay_index == events.size()) { - stopReplay(); - cancel(); - } - } else if (replay_index == events.size()) { - replay_index = 0; - } - } - }; - - sendMessage(Text.translatable("message.repeating-mod.replay_start")); - } - - public void stopReplay() { - is_recording = false; - is_replaying = false; - if (replay_tick != null) { - replay_tick.cancel(); - replay_tick = null; - } - try { - now_record.save(); - } catch (IOException e) { - throw new RuntimeException(e); - } - menu.updateButtons(); - record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); - sendMessage(Text.translatable("message.repeating-mod.replay_stop")); - } - - public static void sendMessage(MutableText text) { - client.player.sendMessage(Text.literal("[") - .append(Text.translatable("text.repeating-mod.name")) - .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) - .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); - } - - public static void sendDebug(String s) { - client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); - } -} +package themixray.repeating.mod; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.Vec3d; +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import themixray.repeating.mod.event.events.DelayEvent; +import themixray.repeating.mod.event.RecordEvent; +import themixray.repeating.mod.event.events.InputEvent; +import themixray.repeating.mod.event.events.MoveEvent; +import themixray.repeating.mod.render.RenderHelper; +import themixray.repeating.mod.render.RenderSystem; +import themixray.repeating.mod.render.buffer.WorldBuffer; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.List; + +public class Main implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); + public static final MinecraftClient client = MinecraftClient.getInstance(); + public static final FabricLoader loader = FabricLoader.getInstance(); + public static Main me; + + public RecordList record_list; + public RecordState now_record; + + public boolean is_recording = false; + public long last_record = -1; + public TickTask move_tick = null; + + public TickTask replay_tick = null; + public boolean is_replaying = false; + public boolean loop_replay = false; + public static InputEvent input_replay = null; + + public long living_ticks = 0; + + public static RepeatingScreen menu; + private static KeyBinding menu_key; + private static KeyBinding toggle_replay_key; + private static KeyBinding toggle_record_key; + + public long record_pos_delay = 20; + + public static Random rand = new Random(); + + public EasyConfig conf; + public File records_folder; + + @Override + public void onInitializeClient() { + LOGGER.info("Repeating mod initialized"); + me = this; + + now_record = null; + + records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); + if (!records_folder.exists()) records_folder.mkdir(); + + record_list = new RecordList(records_folder); + record_list.loadRecords(); + + RenderSystem.init(); + WorldRenderEvents.LAST.register(context -> { + WorldBuffer buffer = RenderHelper.startTri(context); + if (now_record != null) { + Vec3d start_pos = now_record.getStartRecordPos(); + Vec3d finish_pos = now_record.getFinishRecordPos(); + + if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128)); + if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128)); + } + RenderHelper.endTri(buffer); + }); + + ClientTickEvents.END_CLIENT_TICK.register(client -> { + TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); + }); + + Map def = new HashMap<>(); + def.put("record_pos_delay", String.valueOf(record_pos_delay)); + + conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); + + record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); + + menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.menu",InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_J,"text.repeating-mod.name")); + toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, + -1,"text.repeating-mod.name")); + toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, + -1,"text.repeating-mod.name")); + + menu = new RepeatingScreen(); + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (menu_key.wasPressed()) + client.setScreen(menu); + if (toggle_replay_key.wasPressed()) { + if (now_record != null) { + if (!is_recording) { + if (is_replaying) + stopReplay(); + else startReplay(); + menu.updateButtons(); + } + } + } + if (toggle_record_key.wasPressed()) { + if (!is_replaying) { + if (is_recording) + stopRecording(); + else startRecording(); + menu.updateButtons(); + } + } + }); + + new TickTask(0,0) { + @Override + public void run() { + living_ticks++; + } + }; + + System.setProperty("java.awt.headless", "false"); + } + + public void setNowRecord(RecordState record) { + now_record = record; + } + + public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { + RenderHelper.drawRectFromTri(buffer, + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() - 0.25F, + + (float) pos.getX() + 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + + (float) pos.getX() - 0.25F, + (float) pos.getY() + 0.01F, + (float) pos.getZ() + 0.25F, + color); + } + + public void startRecording() { + is_recording = true; + menu.updateButtons(); + + now_record = record_list.newRecord(); + + Vec3d start_pos = client.player.getPos(); + now_record.addEvent(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); + now_record.setStartRecordPos(start_pos); + + if (record_pos_delay > 0) { + move_tick = new TickTask( + record_pos_delay, + record_pos_delay) { + @Override + public void run() { + now_record.addEvent(new MoveEvent(client.player.getPos(), + client.player.getHeadYaw(), client.player.getPitch())); + } + }; + } + + sendMessage(Text.translatable("message.repeating-mod.record_start")); + } + + public void recordTick(RecordEvent e) { + if (is_recording) { + long now = living_ticks; + if (last_record != -1) { + long diff = now - last_record - 2; + if (diff > 0) now_record.addEvent(new DelayEvent(diff)); + } + now_record.addEvent(e); + last_record = now; + } + } + + public void recordAllInput() { + if (client.player == null) { + stopRecording(); + return; + } + + InputEvent l = ((InputEvent) now_record.getLastEvent("input")); + if (l == null) { + InputEvent e = new InputEvent( + client.player.input.sneaking, + client.player.input.jumping, + client.player.input.movementSideways, + client.player.input.movementForward, + client.player.input.pressingForward, + client.player.input.pressingBack, + client.player.input.pressingLeft, + client.player.input.pressingRight, + client.player.getHeadYaw(), + client.player.getBodyYaw(), + client.player.getPitch(), + client.player.isSprinting(), + client.player.getYaw(), + client.player.getMovementSpeed()); + recordTick(e); + } else { + InputEvent e = new InputEvent( + ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, + ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, + (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, + (((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, + ((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, + ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, + ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, + ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, + client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), + ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), + client.player.getYaw(),client.player.getMovementSpeed()); + + if (!(e.isEmpty() && + e.yaw == l.yaw && + e.head_yaw == l.head_yaw && + e.pitch == l.pitch && + e.body_yaw == l.body_yaw)) { + e.fillEmpty(l); + recordTick(e); + } + } + } + + public void stopRecording() { + is_recording = false; + now_record.setFinishRecordPos(client.player.getPos()); + try { + now_record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (move_tick != null) { + move_tick.cancel(); + move_tick = null; + } + menu.updateButtons(); + last_record = -1; + sendMessage(Text.translatable("message.repeating-mod.record_stop")); + } + + + public void startReplay() { + is_recording = false; + is_replaying = true; + menu.updateButtons(); + + List events = now_record.getEvents(); + + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { + public int replay_index = 0; + + @Override + public void run() { + if (!is_replaying) { + cancel(); + return; + } + + RecordEvent e = events.get(replay_index); + if (e != null) { + if (e instanceof DelayEvent) { + setDelay(((DelayEvent) e).delay); + } else { + e.replay(); + } + } + + replay_index++; + if (!loop_replay) { + if (replay_index >= events.size()) { + stopReplay(); + cancel(); + } + } else if (replay_index >= events.size()) { + replay_index = 0; + } + } + }; + + sendMessage(Text.translatable("message.repeating-mod.replay_start")); + } + + public void stopReplay() { + is_recording = false; + is_replaying = false; + if (replay_tick != null) { + replay_tick.cancel(); + replay_tick = null; + } + try { + now_record.save(); + } catch (IOException e) { + throw new RuntimeException(e); + } + menu.updateButtons(); + record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); + sendMessage(Text.translatable("message.repeating-mod.replay_stop")); + } + + public static void sendMessage(MutableText text) { + client.player.sendMessage(Text.literal("[") + .append(Text.translatable("text.repeating-mod.name")) + .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) + .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); + } + + public static void sendDebug(String s) { + client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); + } +} diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/themixray/repeating/mod/RecordList.java index 99fffe7..431a7be 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/themixray/repeating/mod/RecordList.java @@ -51,6 +51,7 @@ public class RecordList { } public void addRecord(RecordState record) { + if (record == null) return; records.add(record); widget.addWidget(record); } diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/themixray/repeating/mod/RecordState.java index 21d782b..782c494 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/themixray/repeating/mod/RecordState.java @@ -96,6 +96,7 @@ public class RecordState { } public void addEvent(RecordEvent event) { + if (event == null) return; events.add(event); } @@ -124,9 +125,10 @@ public class RecordState { .append(finish_record_pos.getY()).append("n") .append(finish_record_pos.getZ()); - for (int i = 0; i < events.size(); i++) { + for (RecordEvent event : events) { + if (event == null) continue; text.append("\n"); - text.append(events.get(i).serialize()); + text.append(event.serialize()); } Files.write(file.toPath(), text.toString().getBytes()); diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 0fcd5ec..015feaf 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -1,187 +1,187 @@ -package themixray.repeating.mod; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.SliderWidget; -import net.minecraft.text.Text; -import themixray.repeating.mod.widget.RecordListWidget; - -import java.awt.*; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -@Environment(EnvType.CLIENT) -public class RepeatingScreen extends Screen { - private static List render_listeners = new ArrayList<>(); - - public ButtonWidget record_btn; - public ButtonWidget loop_btn; - public ButtonWidget import_btn; - - public SliderWidget pos_delay_slider; - - public boolean was_build = false; - - protected RepeatingScreen() { - super(Text.empty()); - } - - public static void addRenderListener(RenderListener render) { - render_listeners.add(render); - } - - public static void removeRenderListener(RenderListener render) { - render_listeners.remove(render); - } - - public void updateButtons() { - if (was_build) { - record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); - loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); - } - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context); - - for (RenderListener l : render_listeners) { - if (l.beforeRender()) { - l.render(context, mouseX, mouseY, delta); - } - } - - super.render(context, mouseX, mouseY, delta); - - for (RenderListener l : render_listeners) { - if (!l.beforeRender()) { - l.render(context, mouseX, mouseY, delta); - } - } - } - - @Override - protected void init() { - RecordListWidget list_widget = Main.me.record_list.getWidget(); - - list_widget.setX(width / 2 + 2); - list_widget.setY(height / 2 - list_widget.getHeight() / 2); - list_widget.init(this); - - - record_btn = ButtonWidget.builder( - Text.translatable("text.repeating-mod.start_record"), button -> { - if (!Main.me.is_replaying) { - if (Main.me.is_recording) - Main.me.stopRecording(); - else Main.me.startRecording(); - updateButtons(); - } - }) - .dimensions(width / 2 - 120, height / 2 - 32, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) - .build(); - - loop_btn = ButtonWidget.builder(Text.empty(), button -> { - Main.me.loop_replay = !Main.me.loop_replay; - updateButtons(); - }) - .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) - .build(); - - pos_delay_slider = new SliderWidget( - width / 2 - 120, height / 2 + 12, 120, 20, - (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), - (Main.me.record_pos_delay+1d)/101d) { - - @Override - protected void updateMessage() { - double v = value*101d-1d; - if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - } - - @Override - protected void applyValue() { - double v = value*101d-1d; - if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); - else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - Main.me.record_pos_delay = (long) v; - Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); - Main.me.conf.save(); - } - - @Override - public void onRelease(double mouseX, double mouseY) { - super.onRelease(mouseX, mouseY); - applyValue(); - } - - @Override - protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { - super.onDrag(mouseX, mouseY, deltaX, deltaY); - applyValue(); - } - }; - pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); - - import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { - new Thread(() -> { - FileDialog fd = new FileDialog((java.awt.Frame) null); - fd.setMultipleMode(true); - fd.setName("Choose record files"); - fd.setTitle("Choose record files"); - fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); - fd.setVisible(true); - - File[] files = fd.getFiles(); - if (files != null) { - for (File file : files) { - try { - Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }}).start(); - }) - .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) - .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) - .build(); - - was_build = true; - - updateButtons(); - - addDrawableChild(loop_btn); - addDrawableChild(record_btn); - addDrawableChild(import_btn); - addDrawableChild(pos_delay_slider); - } - - public T addDrawableChild(T drawableElement) { - return super.addDrawableChild(drawableElement); - } - - public T addDrawable(T drawable) { - return super.addDrawable(drawable); - } - - public T addSelectableChild(T child) { - return super.addSelectableChild(child); - } - - public void remove(Element child) { - super.remove(child); - } -} +package themixray.repeating.mod; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.SliderWidget; +import net.minecraft.text.Text; +import themixray.repeating.mod.widget.RecordListWidget; + +import java.awt.*; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +@Environment(EnvType.CLIENT) +public class RepeatingScreen extends Screen { + private static List render_listeners = new ArrayList<>(); + + public ButtonWidget record_btn; + public ButtonWidget loop_btn; + public ButtonWidget import_btn; + + public SliderWidget pos_delay_slider; + + public boolean was_build = false; + + protected RepeatingScreen() { + super(Text.empty()); + } + + public static void addRenderListener(RenderListener render) { + render_listeners.add(render); + } + + public static void removeRenderListener(RenderListener render) { + render_listeners.remove(render); + } + + public void updateButtons() { + if (was_build) { + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); + loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); + } + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context); + + for (RenderListener l : render_listeners) { + if (l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } + + super.render(context, mouseX, mouseY, delta); + + for (RenderListener l : render_listeners) { + if (!l.beforeRender()) { + l.render(context, mouseX, mouseY, delta); + } + } + } + + @Override + protected void init() { + RecordListWidget list_widget = Main.me.record_list.getWidget(); + + list_widget.setX(width / 2 + 2); + list_widget.setY(height / 2 - list_widget.getHeight() / 2); + list_widget.init(this); + + + record_btn = ButtonWidget.builder( + Text.translatable("text.repeating-mod.start_record"), button -> { + if (!Main.me.is_replaying) { + if (Main.me.is_recording) + Main.me.stopRecording(); + else Main.me.startRecording(); + updateButtons(); + } + }) + .dimensions(width / 2 - 120, height / 2 - 32, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) + .build(); + + loop_btn = ButtonWidget.builder(Text.empty(), button -> { + Main.me.loop_replay = !Main.me.loop_replay; + updateButtons(); + }) + .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) + .build(); + + pos_delay_slider = new SliderWidget( + width / 2 - 120, height / 2 + 12, 120, 20, + (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), + (Main.me.record_pos_delay+1d)/101d) { + + @Override + protected void updateMessage() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); + } + + @Override + protected void applyValue() { + double v = value*101d-1d; + if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); + else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); + Main.me.record_pos_delay = (long) v; + Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); + Main.me.conf.save(); + } + + @Override + public void onRelease(double mouseX, double mouseY) { + super.onRelease(mouseX, mouseY); + applyValue(); + } + + @Override + protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { + super.onDrag(mouseX, mouseY, deltaX, deltaY); + applyValue(); + } + }; + pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); + + import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { + new Thread(() -> { + FileDialog fd = new FileDialog((java.awt.Frame) null); + fd.setMultipleMode(true); + fd.setName("Choose record files"); + fd.setTitle("Choose record files"); + fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); + fd.setVisible(true); + + File[] files = fd.getFiles(); + if (files != null) { + for (File file : files) { + try { + Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }}).start(); + }) + .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) + .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) + .build(); + + was_build = true; + + updateButtons(); + + addDrawableChild(loop_btn); + addDrawableChild(record_btn); + addDrawableChild(import_btn); + addDrawableChild(pos_delay_slider); + } + + public T addDrawableChild(T drawableElement) { + return super.addDrawableChild(drawableElement); + } + + public T addDrawable(T drawable) { + return super.addDrawable(drawable); + } + + public T addSelectableChild(T child) { + return super.addSelectableChild(child); + } + + public void remove(Element child) { + super.remove(child); + } +} diff --git a/src/main/java/themixray/repeating/mod/TickTask.java b/src/main/java/themixray/repeating/mod/TickTask.java index e014511..c1ff672 100644 --- a/src/main/java/themixray/repeating/mod/TickTask.java +++ b/src/main/java/themixray/repeating/mod/TickTask.java @@ -1,95 +1,95 @@ -package themixray.repeating.mod; - -import java.util.ArrayList; -import java.util.List; - -public abstract class TickTask implements Runnable { - public static List tasks = new ArrayList<>(); - - public static void tickTasks(TickAt at) { - for (TickTask t:new ArrayList<>(tasks)) - if (t.getAt() == at) t.tick(); - } - - private long living; - private long delay; - - private boolean is_repeating; - private long period; - - private boolean is_cancelled; - private TickAt at; - - public enum TickAt { - CLIENT_HEAD, CLIENT_TAIL, - MOVEMENT_HEAD, MOVEMENT_TAIL, - RENDER_HEAD, RENDER_TAIL, - CLIENT_EVENT - } - - public TickTask(long delay, TickAt at) { - this.is_cancelled = false; - this.is_repeating = false; - this.delay = delay; - this.living = 0; - this.period = 0; - this.at = at; - tasks.add(this); - } - - public TickTask(long delay, long period, TickAt at) { - this.is_cancelled = false; - this.is_repeating = true; - this.delay = delay; - this.period = period; - this.living = 0; - this.at = at; - tasks.add(this); - } - - public TickTask(long delay) { - this(delay,TickAt.CLIENT_HEAD); - } - - public TickTask(long delay, long period) { - this(delay,period,TickAt.CLIENT_HEAD); - } - - public void cancel() { - if (!is_cancelled) { - is_cancelled = true; - tasks.remove(this); - } - } - - public boolean isCancelled() { - return is_cancelled; - } - - public TickAt getAt() { - return at; - } - - public void setDelay(long delay) { - if (is_repeating) { - this.delay = delay; - } - } - public long getDelay() { - return this.delay; - } - - public void tick() { - if (living >= delay) { - if (is_repeating) { - delay = period; - run(); - living = -1; - } else { - run(); - cancel(); - } - } - living++; - } -} +package themixray.repeating.mod; + +import java.util.ArrayList; +import java.util.List; + +public abstract class TickTask implements Runnable { + public static List tasks = new ArrayList<>(); + + public static void tickTasks(TickAt at) { + for (TickTask t:new ArrayList<>(tasks)) + if (t.getAt() == at) t.tick(); + } + + private long living; + private long delay; + + private boolean is_repeating; + private long period; + + private boolean is_cancelled; + private TickAt at; + + public enum TickAt { + CLIENT_HEAD, CLIENT_TAIL, + MOVEMENT_HEAD, MOVEMENT_TAIL, + RENDER_HEAD, RENDER_TAIL, + CLIENT_EVENT + } + + public TickTask(long delay, TickAt at) { + this.is_cancelled = false; + this.is_repeating = false; + this.delay = delay; + this.living = 0; + this.period = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay, long period, TickAt at) { + this.is_cancelled = false; + this.is_repeating = true; + this.delay = delay; + this.period = period; + this.living = 0; + this.at = at; + tasks.add(this); + } + + public TickTask(long delay) { + this(delay,TickAt.CLIENT_HEAD); + } + + public TickTask(long delay, long period) { + this(delay,period,TickAt.CLIENT_HEAD); + } + + public void cancel() { + if (!is_cancelled) { + is_cancelled = true; + tasks.remove(this); + } + } + + public boolean isCancelled() { + return is_cancelled; + } + + public TickAt getAt() { + return at; + } + + public void setDelay(long delay) { + if (is_repeating) { + this.delay = delay; + } + } + public long getDelay() { + return this.delay; + } + + public void tick() { + if (living >= delay) { + if (is_repeating) { + delay = period; + run(); + living = -1; + } else { + run(); + cancel(); + } + } + living++; + } +} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java b/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java index 470c29c..720e027 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java +++ b/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java @@ -16,7 +16,7 @@ public class GuiMouseScrollEvent extends RecordEvent { public void replay() { if (Main.client.currentScreen != null) { - Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount); +// Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount); } } diff --git a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java index 60e9e64..b3adac0 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java @@ -1,24 +1,24 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.MinecraftClient; -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.Main; -import themixray.repeating.mod.TickTask; - -@Mixin(MinecraftClient.class) -public abstract class ClientMixin { - @Inject(at = @At(value = "HEAD"), method = "tick") - private void onTickHead(CallbackInfo ci) { - if (Main.me.is_recording) - Main.me.recordAllInput(); - TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); - } -} +package themixray.repeating.mod.mixin; + +import net.minecraft.client.MinecraftClient; +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.Main; +import themixray.repeating.mod.TickTask; + +@Mixin(MinecraftClient.class) +public abstract class ClientMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + if (Main.me.is_recording) + Main.me.recordAllInput(); + TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java index eed7b58..185de64 100644 --- a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java @@ -1,31 +1,31 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.entity.Entity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import themixray.repeating.mod.Main; - -import java.util.UUID; - -@Mixin(Entity.class) -public abstract class EntityMixin { - @Shadow public abstract UUID getUuid(); - - @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) - private void onSprint(boolean sprinting,CallbackInfo ci) { - if (Main.client.player != null) { - if (getUuid().equals(Main.client.player.getUuid())) { - if (Main.me.is_replaying) { - if (Main.input_replay != null && - Main.input_replay.sprinting != null && - Main.input_replay.sprinting != sprinting) { - ci.cancel(); - } - } - } - } - } -} +package themixray.repeating.mod.mixin; + +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import themixray.repeating.mod.Main; + +import java.util.UUID; + +@Mixin(Entity.class) +public abstract class EntityMixin { + @Shadow public abstract UUID getUuid(); + + @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) + private void onSprint(boolean sprinting,CallbackInfo ci) { + if (Main.client.player != null) { + if (getUuid().equals(Main.client.player.getUuid())) { + if (Main.me.is_replaying) { + if (Main.input_replay != null && + Main.input_replay.sprinting != null && + Main.input_replay.sprinting != sprinting) { + ci.cancel(); + } + } + } + } + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java index 800e147..c58f53c 100644 --- a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/InputMixin.java @@ -1,20 +1,20 @@ -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.Main; - -@Mixin(KeyboardInput.class) -public abstract class InputMixin { - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { - if (Main.me.is_replaying) { - if (Main.input_replay != null) { - Main.input_replay.inputCallback(); - } - } - } -} +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.Main; + +@Mixin(KeyboardInput.class) +public abstract class InputMixin { + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { + if (Main.me.is_replaying) { + if (Main.input_replay != null) { + Main.input_replay.inputCallback(); + } + } + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java index b850676..f9084e7 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java @@ -1,44 +1,44 @@ -package themixray.repeating.mod.mixin; - -import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.hit.HitResult; -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.Main; -import themixray.repeating.mod.event.events.BlockBreakEvent; -import themixray.repeating.mod.event.events.BlockInteractEvent; -import themixray.repeating.mod.TickTask; - -@Mixin(ClientPlayerEntity.class) -public abstract class MovementMixin { - - @Inject(at = @At(value = "HEAD"), method = "init") - private void init(CallbackInfo ci) { - PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { - if (Main.me.is_recording) - Main.me.recordTick(new BlockBreakEvent(pos)); - }); - - UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { - if (hitResult.getType().equals(HitResult.Type.BLOCK)) - if (Main.me.is_recording) - Main.me.recordTick(new BlockInteractEvent(hand,hitResult)); - return ActionResult.PASS; - }); - } - - @Inject(at = @At(value = "HEAD"), method = "tickMovement") - private void onMoveHead(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onMoveTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); - } -} +package themixray.repeating.mod.mixin; + +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.HitResult; +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.Main; +import themixray.repeating.mod.event.events.BlockBreakEvent; +import themixray.repeating.mod.event.events.BlockInteractEvent; +import themixray.repeating.mod.TickTask; + +@Mixin(ClientPlayerEntity.class) +public abstract class MovementMixin { + + @Inject(at = @At(value = "HEAD"), method = "init") + private void init(CallbackInfo ci) { + PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { + if (Main.me.is_recording) + Main.me.recordTick(new BlockBreakEvent(pos)); + }); + + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { + if (hitResult.getType().equals(HitResult.Type.BLOCK)) + if (Main.me.is_recording) + Main.me.recordTick(new BlockInteractEvent(hand,hitResult)); + return ActionResult.PASS; + }); + } + + @Inject(at = @At(value = "HEAD"), method = "tickMovement") + private void onMoveHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onMoveTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java index 4f85878..396b6c2 100644 --- a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java @@ -1,29 +1,29 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.network.listener.ServerPlayPacketListener; -import net.minecraft.network.packet.Packet; -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 java.time.Duration; -import java.util.function.BooleanSupplier; - -@Mixin(ClientPlayNetworkHandler.class) -public abstract class NetworkMixin { -// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") -// private void onSendPacket1Head(Packet packet, -// CallbackInfo ci) { -// -// } -// -// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") -// private void onSendPacket2Head(Packet packet, -// BooleanSupplier sendCondition, -// Duration expirationTime, -// CallbackInfo ci) { -// -// } -} +package themixray.repeating.mod.mixin; + +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.network.listener.ServerPlayPacketListener; +import net.minecraft.network.packet.Packet; +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 java.time.Duration; +import java.util.function.BooleanSupplier; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class NetworkMixin { +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") +// private void onSendPacket1Head(Packet packet, +// CallbackInfo ci) { +// +// } +// +// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") +// private void onSendPacket2Head(Packet packet, +// BooleanSupplier sendCondition, +// Duration expirationTime, +// CallbackInfo ci) { +// +// } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java index edb363d..22ca225 100644 --- a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java @@ -1,21 +1,21 @@ -package themixray.repeating.mod.mixin; - -import net.minecraft.client.render.*; -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.TickTask; - -@Mixin(GameRenderer.class) -public abstract class RendererMixin { - @Inject(at = @At(value = "HEAD"), method = "tick") - private void onTickHead(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); - } - - @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(CallbackInfo ci) { - TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); - } -} +package themixray.repeating.mod.mixin; + +import net.minecraft.client.render.*; +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.TickTask; + +@Mixin(GameRenderer.class) +public abstract class RendererMixin { + @Inject(at = @At(value = "HEAD"), method = "tick") + private void onTickHead(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); + } + + @Inject(at = @At(value = "TAIL"), method = "tick") + private void onTickTail(CallbackInfo ci) { + TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); + } +} diff --git a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java index bfa46c8..63d5fc5 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java +++ b/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java @@ -80,11 +80,11 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa return super.mouseReleased(mouseX, mouseY, button); } - @Override - public boolean mouseScrolled(double mouseX, double mouseY, double amount) { - if (Main.me.is_recording) { -// Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount)); - } - return super.mouseScrolled(mouseX, mouseY, amount); - } +// @Override +// public boolean mouseScrolled(double mouseX, double mouseY, double amount) { +// if (Main.me.is_recording) { +//// Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount)); +// } +// return super.mouseScrolled(mouseX, mouseY, amount); +// } } diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java index 3838168..719a606 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/themixray/repeating/mod/widget/RecordWidget.java @@ -134,7 +134,8 @@ public class RecordWidget implements Drawable, Widget { children.add(export_button); - ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.start"), (i) -> { + ButtonWidget replay_button = ButtonWidget.builder(Text.translatable("text.repeating-mod." + + (getRecord().equals(Main.me.now_record) && Main.me.is_replaying ? "stop" : "start")), (i) -> { if (Main.me.is_replaying) { Main.me.stopReplay(); if (getRecord().equals(Main.me.now_record)) { @@ -143,7 +144,7 @@ public class RecordWidget implements Drawable, Widget { } i.setMessage(Text.translatable("text.repeating-mod.stop")); - Main.me.now_record = record; + Main.me.now_record = getRecord(); Main.me.startReplay(); Main.client.setScreen(null); }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) diff --git a/src/main/resources/assets/minecraft/font/default.json b/src/main/resources/assets/minecraft/font/default.json index 6501fb4..ef1f683 100644 --- a/src/main/resources/assets/minecraft/font/default.json +++ b/src/main/resources/assets/minecraft/font/default.json @@ -1,17 +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" - } - ] +{ + "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/lang/en_us.json b/src/main/resources/assets/repeating-mod/lang/en_us.json index 774b7d8..d2f69be 100644 --- a/src/main/resources/assets/repeating-mod/lang/en_us.json +++ b/src/main/resources/assets/repeating-mod/lang/en_us.json @@ -1,36 +1,36 @@ -{ - "key.repeating-mod.menu": "Repeating menu", - "key.repeating-mod.toggle_replay": "Toggle replay", - "key.repeating-mod.toggle_record": "Toggle recording", - - "text.repeating-mod.name": "Repeating Mod", - "text.repeating-mod.start_record": "Start record", - "text.repeating-mod.stop_record": "Stop record", - "text.repeating-mod.start_replay": "Start replay", - "text.repeating-mod.stop_replay": "Stop replay", - "text.repeating-mod.record_tooltip": "Start/stop recording all activities", - "text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions", - "text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay", - "text.repeating-mod.export_record": "Export record", - "text.repeating-mod.import": "Import record", - "text.repeating-mod.export_tooltip": "Exporting a recording to a file", - "text.repeating-mod.import_tooltip": "Importing a recording from a file", - "text.repeating-mod.dev": "In development...", - "text.repeating-mod.nan_pos_delay": "No pos timer", - "text.repeating-mod.pos_delay": "Pos timer: %s ticks", - "text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", - "text.repeating-mod.unnamed": "Unnamed Record", - "text.repeating-mod.on_loop": "Enable repeat", - "text.repeating-mod.off_loop": "Disable repeat", - "text.repeating-mod.recorded_at": "Recorded at", - "text.repeating-mod.author": "Author", - "text.repeating-mod.delete": "Delete", - "text.repeating-mod.start": "Start", - "text.repeating-mod.stop": "Stop", - "text.repeating-mod.export": "Export", - - "message.repeating-mod.replay_start": "Replay started", - "message.repeating-mod.replay_stop": "Replay finished", - "message.repeating-mod.record_start": "Record started", - "message.repeating-mod.record_stop": "Record finished" +{ + "key.repeating-mod.menu": "Repeating menu", + "key.repeating-mod.toggle_replay": "Toggle replay", + "key.repeating-mod.toggle_record": "Toggle recording", + + "text.repeating-mod.name": "Repeating Mod", + "text.repeating-mod.start_record": "Start record", + "text.repeating-mod.stop_record": "Stop record", + "text.repeating-mod.start_replay": "Start replay", + "text.repeating-mod.stop_replay": "Stop replay", + "text.repeating-mod.record_tooltip": "Start/stop recording all activities", + "text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions", + "text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay", + "text.repeating-mod.export_record": "Export record", + "text.repeating-mod.import": "Import record", + "text.repeating-mod.export_tooltip": "Exporting a recording to a file", + "text.repeating-mod.import_tooltip": "Importing a recording from a file", + "text.repeating-mod.dev": "In development...", + "text.repeating-mod.nan_pos_delay": "No pos timer", + "text.repeating-mod.pos_delay": "Pos timer: %s ticks", + "text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)", + "text.repeating-mod.unnamed": "Unnamed Record", + "text.repeating-mod.on_loop": "Enable repeat", + "text.repeating-mod.off_loop": "Disable repeat", + "text.repeating-mod.recorded_at": "Recorded at", + "text.repeating-mod.author": "Author", + "text.repeating-mod.delete": "Delete", + "text.repeating-mod.start": "Start", + "text.repeating-mod.stop": "Stop", + "text.repeating-mod.export": "Export", + + "message.repeating-mod.replay_start": "Replay started", + "message.repeating-mod.replay_stop": "Replay finished", + "message.repeating-mod.record_start": "Record started", + "message.repeating-mod.record_stop": "Record finished" } \ No newline at end of file diff --git a/src/main/resources/assets/repeating-mod/lang/ru_ru.json b/src/main/resources/assets/repeating-mod/lang/ru_ru.json index 2cf3521..84bf569 100644 --- a/src/main/resources/assets/repeating-mod/lang/ru_ru.json +++ b/src/main/resources/assets/repeating-mod/lang/ru_ru.json @@ -1,38 +1,38 @@ -{ - "key.repeating-mod.menu": "Меню репитинга", - "key.repeating-mod.toggle_replay": "Вкл/выкл повтор", - "key.repeating-mod.toggle_record": "Вкл/выкл запись", - - "text.repeating-mod.name": "Репитинг Мод", - "text.repeating-mod.start_record": "Начать запись", - "text.repeating-mod.stop_record": "Остановить запись", - "text.repeating-mod.start_replay": "Начать повтор", - "text.repeating-mod.stop_replay": "Остановить повтор", - "text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий", - "text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий", - "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор записи", - "text.repeating-mod.import": "Импорт записи", - "text.repeating-mod.export_tooltip": "Экспорт записи в файл", - "text.repeating-mod.import_tooltip": "Импорт записи из файла", - "text.repeating-mod.dev": "В разработке...", - "text.repeating-mod.nan_pos_delay": "Таймера позиции нету", - "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", - "text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", - "text.repeating-mod.unnamed": "Безымянная Запись", - "text.repeating-mod.on_loop": "Включить повторение", - "text.repeating-mod.off_loop": "Выключить повторение", - "text.repeating-mod.recorded_at": "Записано в", - "text.repeating-mod.author": "Автор", - - "message.repeating-mod.replay_start": "Повтор начат", - "message.repeating-mod.replay_stop": "Повтор закончен", - "message.repeating-mod.record_start": "Запись начата", - "message.repeating-mod.record_stop": "Запись закончена", - - "text.repeating-mod.export_record": "Экпорт записи", - "text.repeating-mod.export": "Экспорт", - "text.repeating-mod.delete": "Удалить", - "text.repeating-mod.start": "Старт", - "text.repeating-mod.stop": "Стоп" -} - +{ + "key.repeating-mod.menu": "Меню репитинга", + "key.repeating-mod.toggle_replay": "Вкл/выкл повтор", + "key.repeating-mod.toggle_record": "Вкл/выкл запись", + + "text.repeating-mod.name": "Репитинг Мод", + "text.repeating-mod.start_record": "Начать запись", + "text.repeating-mod.stop_record": "Остановить запись", + "text.repeating-mod.start_replay": "Начать повтор", + "text.repeating-mod.stop_replay": "Остановить повтор", + "text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий", + "text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий", + "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор записи", + "text.repeating-mod.import": "Импорт записи", + "text.repeating-mod.export_tooltip": "Экспорт записи в файл", + "text.repeating-mod.import_tooltip": "Импорт записи из файла", + "text.repeating-mod.dev": "В разработке...", + "text.repeating-mod.nan_pos_delay": "Таймера позиции нету", + "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", + "text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", + "text.repeating-mod.unnamed": "Безымянная Запись", + "text.repeating-mod.on_loop": "Включить повторение", + "text.repeating-mod.off_loop": "Выключить повторение", + "text.repeating-mod.recorded_at": "Записано в", + "text.repeating-mod.author": "Автор", + + "message.repeating-mod.replay_start": "Повтор начат", + "message.repeating-mod.replay_stop": "Повтор закончен", + "message.repeating-mod.record_start": "Запись начата", + "message.repeating-mod.record_stop": "Запись закончена", + + "text.repeating-mod.export_record": "Экпорт записи", + "text.repeating-mod.export": "Экспорт", + "text.repeating-mod.delete": "Удалить", + "text.repeating-mod.start": "Старт", + "text.repeating-mod.stop": "Стоп" +} + diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 7a2119f..2e09cc1 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,38 +1,38 @@ -{ - "schemaVersion": 1, - "id": "repeating-mod", - "version": "${version}", - - "name": "Repeating Mod", - "description": "Mod that repeats your recorded actions. ", - "authors": [ - "TheMixRay" - ], - "contact": { - "homepage": "https://modrinth.com/mod/repeating-mod", - "sources": "https://github.com/MeexReay/repeating-mod" - }, - - "license": "CC0-1.0", - "icon": "icon.png", - - "environment": "client", - "entrypoints": { - "client": [ - "themixray.repeating.mod.Main" - ] - }, - "mixins": [ - "repeating-mod.mixins.json" - ], - - "depends": { - "fabricloader": ">=0.14.14", - "fabric-api": "*", - "minecraft": ">=1.20", - "java": ">=17" - }, - "suggests": { - "another-mod": "*" - } -} +{ + "schemaVersion": 1, + "id": "repeating-mod", + "version": "${version}", + + "name": "Repeating Mod", + "description": "Mod that repeats your recorded actions. ", + "authors": [ + "TheMixRay" + ], + "contact": { + "homepage": "https://modrinth.com/mod/repeating-mod", + "sources": "https://github.com/MeexReay/repeating-mod" + }, + + "license": "CC0-1.0", + "icon": "icon.png", + + "environment": "client", + "entrypoints": { + "client": [ + "themixray.repeating.mod.Main" + ] + }, + "mixins": [ + "repeating-mod.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.14.14", + "fabric-api": "*", + "minecraft": ">=1.20", + "java": ">=17" + }, + "suggests": { + "another-mod": "*" + } +} diff --git a/src/main/resources/repeating-mod.mixins.json b/src/main/resources/repeating-mod.mixins.json index 6156b8f..4196f7e 100644 --- a/src/main/resources/repeating-mod.mixins.json +++ b/src/main/resources/repeating-mod.mixins.json @@ -1,20 +1,20 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "themixray.repeating.mod.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - ], - "client": [ - "MovementMixin", - "InputMixin", - "RendererMixin", - "EntityMixin", - "ClientMixin", - "ScreenMixin", - "PlayerMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} +{ + "required": true, + "minVersion": "0.8", + "package": "themixray.repeating.mod.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "MovementMixin", + "InputMixin", + "RendererMixin", + "EntityMixin", + "ClientMixin", + "ScreenMixin", + "PlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} From 61c755aabc2e63119f389c21881f03311f3afdf5 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 03:02:04 +0300 Subject: [PATCH 46/72] move to 1.21 and fix for nixos --- .gitignore | 42 ++++++++++++++++++ build.gradle | 12 ++--- gradle.properties | 13 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- shell.nix | 6 +++ .../repeating/mod/RepeatingScreen.java | 2 +- .../mod/render/shader/ShaderManager.java | 2 +- 8 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34d600a --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# gradle + +.gradle/ +build/ +out/ +classes/ + +# eclipse + +*.launch + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# macos + +*.DS_Store + +# fabric + +run/ + +# java + +hs_err_*.log +replay_*.log +*.hprof +*.jfr + +remappedSrc/ diff --git a/build.gradle b/build.gradle index 84a69d1..383e374 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.6-SNAPSHOT' + id 'fabric-loom' version "${loom_version}" id 'maven-publish' } @@ -19,8 +19,8 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.24' - annotationProcessor 'org.projectlombok:lombok:1.18.24' + compileOnly 'org.projectlombok:lombok:1.18.38' + annotationProcessor 'org.projectlombok:lombok:1.18.38' //add joml modImplementation 'org.joml:joml:1.10.4' @@ -45,7 +45,7 @@ processResources { } tasks.withType(JavaCompile).configureEach { - it.options.release = 17 + it.options.release = 21 } java { @@ -54,8 +54,8 @@ java { // If you remove this line, sources will not be generated. withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } jar { diff --git a/gradle.properties b/gradle.properties index be55ec6..8feaa20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,15 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.3 -loader_version=0.15.10 +minecraft_version=1.21 +yarn_mappings=1.21+build.9 +loader_version=0.16.14 +loom_version=1.10-SNAPSHOT -#Fabric api -fabric_version=0.97.0+1.20.4 +# Fabric API +fabric_version=0.102.0+1.21 # Mod Properties -mod_version = 1.1.2+1.20.1 +mod_version = 1.1.2+1.21 maven_group = themixray.repeating.mod archives_base_name = repeating-mod \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 12612 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 BT1x-` delta 12460 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 T`Z7J93TASz(qo%C2V^+_d67$> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..e18bc25 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..27efeb0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,6 @@ +{ pkgs ? import {} }: +pkgs.mkShell { + shellHook = '' + export LD_LIBRARY_PATH="''${LD_LIBRARY_PATH}''${LD_LIBRARY_PATH:+:}${pkgs.libglvnd}/lib" + ''; +} \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/themixray/repeating/mod/RepeatingScreen.java index 015feaf..78f2121 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/themixray/repeating/mod/RepeatingScreen.java @@ -51,7 +51,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context); + renderBackground(context, mouseX, mouseY, delta); for (RenderListener l : render_listeners) { if (l.beforeRender()) { diff --git a/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java b/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java index f11c98b..08d6a9b 100644 --- a/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java +++ b/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java @@ -41,7 +41,7 @@ public class ShaderManager { try { boolean file_present = true; ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager(); - Optional resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension)); + Optional resource = resourceFactory.getResource(Identifier.of("renderer", "shader/" + name + type.fileExtension)); int i = glCreateShader(type.glType); if (resource.isPresent()) { GlStateManager.glShaderSource(i, new GlImportProcessor() { From e7abf3e15b688756938614d2b3f3375f33a20e46 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 03:04:16 +0300 Subject: [PATCH 47/72] open source! --- LICENSE | 433 ++------------------------------------------------- build.gradle | 1 - 2 files changed, 9 insertions(+), 425 deletions(-) diff --git a/LICENSE b/LICENSE index 2d58298..07b7a81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,428 +1,13 @@ -Attribution-ShareAlike 4.0 International + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 -======================================================================= +Copyright (C) 2004 Sam Hocevar -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-ShareAlike 4.0 International Public -License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + 0. You just DO WHAT THE FUCK YOU WANT TO. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 383e374..ffbc124 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,6 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - } processResources { From 8242b2c60191773b975ad9a2efb50fa4cf649c33 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 03:13:26 +0300 Subject: [PATCH 48/72] readme update --- README.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f63c38a..43b4faa 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ This mod can record your movements and play them back. +## How to use + +Simply start recording, perform any actions, stop the recording, return to the starting point, and then you can replay everything you did. + +## Showcase + ![Preview gif](preview.gif) ## Controls @@ -10,29 +16,18 @@ Default Repeating Mod keys ``` Menu | J -Toggle recording | not specified -Toggle replay | not specified +Toggle recording | not specified by default +Toggle replay | not specified by default ``` ## Menu -How menu looks like +This is how menu looks like: ![repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/4123068f-b150-45ae-8ae3-fcaa0e6bb9f8) -## Todo -Releases dont have striked lines +## Roadmap -- record gui mouse clicks and key pressing (in dev) -- create new preview.gif -- practice mode like in geometry dash for parkours - -## How to build - -How to build mod from source - -``` -gradlew build -``` - -Compiled .jar file you can find in `build/libs` +- [ ] relative mode for repeating actions (like mining) +- [ ] record mouse and keyboard in gui +- [ ] practice mode (like in geometry dash but for parkours) From 45998bbe2e741689795fbfc3a2ee7863f80aa187 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 05:02:06 +0300 Subject: [PATCH 49/72] rename group-id and artifact-id, fix recording points render --- gradle.properties | 2 +- .../themixray/repeating_mod}/EasyConfig.java | 2 +- .../themixray/repeating_mod}/Main.java | 46 +++++++++---------- .../themixray/repeating_mod}/RecordList.java | 7 +-- .../themixray/repeating_mod}/RecordState.java | 5 +- .../repeating_mod}/RenderListener.java | 2 +- .../repeating_mod}/RepeatingScreen.java | 44 +++++++++--------- .../themixray/repeating_mod}/TickTask.java | 2 +- .../repeating_mod}/event/RecordEvent.java | 4 +- .../repeating_mod}/event/RecordEventType.java | 4 +- .../event/events/BlockBreakEvent.java | 6 +-- .../event/events/BlockInteractEvent.java | 6 +-- .../event/events/DelayEvent.java | 4 +- .../event/events/GuiCharTypeEvent.java | 6 +-- .../event/events/GuiCloseEvent.java | 6 +-- .../event/events/GuiKeyPressEvent.java | 6 +-- .../event/events/GuiKeyReleaseEvent.java | 6 +-- .../event/events/GuiMouseClickEvent.java | 6 +-- .../event/events/GuiMouseDragEvent.java | 6 +-- .../event/events/GuiMouseMoveEvent.java | 6 +-- .../event/events/GuiMouseReleaseEvent.java | 6 +-- .../event/events/GuiMouseScrollEvent.java | 6 +-- .../event/events/InputEvent.java | 6 +-- .../event/events/MoveEvent.java | 6 +-- .../repeating_mod}/mixin/ClientMixin.java | 6 +-- .../repeating_mod}/mixin/EntityMixin.java | 4 +- .../repeating_mod}/mixin/InputMixin.java | 4 +- .../repeating_mod}/mixin/MovementMixin.java | 10 ++-- .../repeating_mod}/mixin/NetworkMixin.java | 2 +- .../repeating_mod}/mixin/PlayerMixin.java | 10 +--- .../repeating_mod}/mixin/RendererMixin.java | 4 +- .../repeating_mod}/mixin/ScreenMixin.java | 11 ++--- .../repeating_mod}/render/RenderHelper.java | 18 ++++---- .../repeating_mod}/render/RenderSystem.java | 6 +-- .../render/buffer/BufferManager.java | 2 +- .../repeating_mod}/render/buffer/Vertex.java | 15 ++---- .../render/buffer/WorldBuffer.java | 39 +++++++--------- .../repeating_mod}/render/shader/Shader.java | 6 +-- .../render/shader/ShaderManager.java | 6 +-- .../widget/RecordListWidget.java | 16 ++----- .../repeating_mod}/widget/RecordWidget.java | 11 ++--- src/main/resources/fabric.mod.json | 2 +- src/main/resources/repeating-mod.mixins.json | 2 +- 43 files changed, 169 insertions(+), 205 deletions(-) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/EasyConfig.java (95%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/Main.java (83%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/RecordList.java (92%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/RecordState.java (98%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/RenderListener.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/RepeatingScreen.java (72%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/TickTask.java (94%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/RecordEvent.java (87%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/RecordEventType.java (95%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/BlockBreakEvent.java (85%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/BlockInteractEvent.java (92%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/DelayEvent.java (84%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiCharTypeEvent.java (84%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiCloseEvent.java (73%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiKeyPressEvent.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiKeyReleaseEvent.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiMouseClickEvent.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiMouseDragEvent.java (89%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiMouseMoveEvent.java (84%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiMouseReleaseEvent.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/GuiMouseScrollEvent.java (86%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/InputEvent.java (98%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/event/events/MoveEvent.java (90%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/ClientMixin.java (82%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/EntityMixin.java (88%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/InputMixin.java (84%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/MovementMixin.java (82%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/NetworkMixin.java (94%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/PlayerMixin.java (64%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/RendererMixin.java (85%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/mixin/ScreenMixin.java (90%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/RenderHelper.java (87%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/RenderSystem.java (52%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/buffer/BufferManager.java (95%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/buffer/Vertex.java (50%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/buffer/WorldBuffer.java (63%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/shader/Shader.java (67%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/render/shader/ShaderManager.java (93%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/widget/RecordListWidget.java (89%) rename src/main/java/{themixray/repeating/mod => ru/themixray/repeating_mod}/widget/RecordWidget.java (96%) diff --git a/gradle.properties b/gradle.properties index 8feaa20..489444b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ fabric_version=0.102.0+1.21 # Mod Properties mod_version = 1.1.2+1.21 -maven_group = themixray.repeating.mod +maven_group = ru.themixray archives_base_name = repeating-mod \ No newline at end of file diff --git a/src/main/java/themixray/repeating/mod/EasyConfig.java b/src/main/java/ru/themixray/repeating_mod/EasyConfig.java similarity index 95% rename from src/main/java/themixray/repeating/mod/EasyConfig.java rename to src/main/java/ru/themixray/repeating_mod/EasyConfig.java index 0ee871c..b592deb 100644 --- a/src/main/java/themixray/repeating/mod/EasyConfig.java +++ b/src/main/java/ru/themixray/repeating_mod/EasyConfig.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/themixray/repeating/mod/Main.java b/src/main/java/ru/themixray/repeating_mod/Main.java similarity index 83% rename from src/main/java/themixray/repeating/mod/Main.java rename to src/main/java/ru/themixray/repeating_mod/Main.java index f213ee2..e5ac5a9 100644 --- a/src/main/java/themixray/repeating/mod/Main.java +++ b/src/main/java/ru/themixray/repeating_mod/Main.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -15,13 +15,13 @@ import net.minecraft.util.math.Vec3d; import org.lwjgl.glfw.GLFW; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import themixray.repeating.mod.event.events.DelayEvent; -import themixray.repeating.mod.event.RecordEvent; -import themixray.repeating.mod.event.events.InputEvent; -import themixray.repeating.mod.event.events.MoveEvent; -import themixray.repeating.mod.render.RenderHelper; -import themixray.repeating.mod.render.RenderSystem; -import themixray.repeating.mod.render.buffer.WorldBuffer; +import ru.themixray.repeating_mod.event.events.DelayEvent; +import ru.themixray.repeating_mod.event.RecordEvent; +import ru.themixray.repeating_mod.event.events.InputEvent; +import ru.themixray.repeating_mod.event.events.MoveEvent; +import ru.themixray.repeating_mod.render.RenderHelper; +import ru.themixray.repeating_mod.render.RenderSystem; +import ru.themixray.repeating_mod.render.buffer.WorldBuffer; import java.awt.*; import java.io.File; @@ -35,21 +35,21 @@ public class Main implements ClientModInitializer { public static final FabricLoader loader = FabricLoader.getInstance(); public static Main me; - public RecordList record_list; - public RecordState now_record; + public ru.themixray.repeating_mod.RecordList record_list; + public ru.themixray.repeating_mod.RecordState now_record; public boolean is_recording = false; public long last_record = -1; - public TickTask move_tick = null; + public ru.themixray.repeating_mod.TickTask move_tick = null; - public TickTask replay_tick = null; + public ru.themixray.repeating_mod.TickTask replay_tick = null; public boolean is_replaying = false; public boolean loop_replay = false; public static InputEvent input_replay = null; public long living_ticks = 0; - public static RepeatingScreen menu; + public static ru.themixray.repeating_mod.RepeatingScreen menu; private static KeyBinding menu_key; private static KeyBinding toggle_replay_key; private static KeyBinding toggle_record_key; @@ -58,7 +58,7 @@ public class Main implements ClientModInitializer { public static Random rand = new Random(); - public EasyConfig conf; + public ru.themixray.repeating_mod.EasyConfig conf; public File records_folder; @Override @@ -71,11 +71,11 @@ public class Main implements ClientModInitializer { records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); if (!records_folder.exists()) records_folder.mkdir(); - record_list = new RecordList(records_folder); + record_list = new ru.themixray.repeating_mod.RecordList(records_folder); record_list.loadRecords(); RenderSystem.init(); - WorldRenderEvents.LAST.register(context -> { + WorldRenderEvents.AFTER_ENTITIES.register(context -> { WorldBuffer buffer = RenderHelper.startTri(context); if (now_record != null) { Vec3d start_pos = now_record.getStartRecordPos(); @@ -88,13 +88,13 @@ public class Main implements ClientModInitializer { }); ClientTickEvents.END_CLIENT_TICK.register(client -> { - TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); + ru.themixray.repeating_mod.TickTask.tickTasks(ru.themixray.repeating_mod.TickTask.TickAt.CLIENT_EVENT); }); Map def = new HashMap<>(); def.put("record_pos_delay", String.valueOf(record_pos_delay)); - conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); + conf = new ru.themixray.repeating_mod.EasyConfig(loader.getConfigDir(),"repeating-mod",def); record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); @@ -108,7 +108,7 @@ public class Main implements ClientModInitializer { "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, -1,"text.repeating-mod.name")); - menu = new RepeatingScreen(); + menu = new ru.themixray.repeating_mod.RepeatingScreen(); ClientTickEvents.END_CLIENT_TICK.register(client -> { if (menu_key.wasPressed()) client.setScreen(menu); @@ -132,7 +132,7 @@ public class Main implements ClientModInitializer { } }); - new TickTask(0,0) { + new ru.themixray.repeating_mod.TickTask(0,0) { @Override public void run() { living_ticks++; @@ -142,7 +142,7 @@ public class Main implements ClientModInitializer { System.setProperty("java.awt.headless", "false"); } - public void setNowRecord(RecordState record) { + public void setNowRecord(ru.themixray.repeating_mod.RecordState record) { now_record = record; } @@ -177,7 +177,7 @@ public class Main implements ClientModInitializer { now_record.setStartRecordPos(start_pos); if (record_pos_delay > 0) { - move_tick = new TickTask( + move_tick = new ru.themixray.repeating_mod.TickTask( record_pos_delay, record_pos_delay) { @Override @@ -277,7 +277,7 @@ public class Main implements ClientModInitializer { List events = now_record.getEvents(); - replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { + replay_tick = new ru.themixray.repeating_mod.TickTask(0,0, ru.themixray.repeating_mod.TickTask.TickAt.CLIENT_EVENT) { public int replay_index = 0; @Override diff --git a/src/main/java/themixray/repeating/mod/RecordList.java b/src/main/java/ru/themixray/repeating_mod/RecordList.java similarity index 92% rename from src/main/java/themixray/repeating/mod/RecordList.java rename to src/main/java/ru/themixray/repeating_mod/RecordList.java index 431a7be..cebbc45 100644 --- a/src/main/java/themixray/repeating/mod/RecordList.java +++ b/src/main/java/ru/themixray/repeating_mod/RecordList.java @@ -1,12 +1,9 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; -import net.minecraft.text.Text; -import themixray.repeating.mod.widget.RecordListWidget; +import ru.themixray.repeating_mod.widget.RecordListWidget; import java.io.File; -import java.io.IOException; import java.nio.file.Files; -import java.text.SimpleDateFormat; import java.util.*; public class RecordList { diff --git a/src/main/java/themixray/repeating/mod/RecordState.java b/src/main/java/ru/themixray/repeating_mod/RecordState.java similarity index 98% rename from src/main/java/themixray/repeating/mod/RecordState.java rename to src/main/java/ru/themixray/repeating_mod/RecordState.java index 782c494..3a684f5 100644 --- a/src/main/java/themixray/repeating/mod/RecordState.java +++ b/src/main/java/ru/themixray/repeating_mod/RecordState.java @@ -1,10 +1,9 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import com.google.common.collect.Lists; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.event.RecordEvent; -import java.awt.*; import java.io.File; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/themixray/repeating/mod/RenderListener.java b/src/main/java/ru/themixray/repeating_mod/RenderListener.java similarity index 86% rename from src/main/java/themixray/repeating/mod/RenderListener.java rename to src/main/java/ru/themixray/repeating_mod/RenderListener.java index 9992e48..80558be 100644 --- a/src/main/java/themixray/repeating/mod/RenderListener.java +++ b/src/main/java/ru/themixray/repeating_mod/RenderListener.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import net.minecraft.client.gui.DrawContext; diff --git a/src/main/java/themixray/repeating/mod/RepeatingScreen.java b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java similarity index 72% rename from src/main/java/themixray/repeating/mod/RepeatingScreen.java rename to src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java index 78f2121..6f4df2c 100644 --- a/src/main/java/themixray/repeating/mod/RepeatingScreen.java +++ b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -11,7 +11,7 @@ import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.text.Text; -import themixray.repeating.mod.widget.RecordListWidget; +import ru.themixray.repeating_mod.widget.RecordListWidget; import java.awt.*; import java.io.File; @@ -20,7 +20,7 @@ import java.util.List; @Environment(EnvType.CLIENT) public class RepeatingScreen extends Screen { - private static List render_listeners = new ArrayList<>(); + private static List render_listeners = new ArrayList<>(); public ButtonWidget record_btn; public ButtonWidget loop_btn; @@ -34,18 +34,18 @@ public class RepeatingScreen extends Screen { super(Text.empty()); } - public static void addRenderListener(RenderListener render) { + public static void addRenderListener(ru.themixray.repeating_mod.RenderListener render) { render_listeners.add(render); } - public static void removeRenderListener(RenderListener render) { + public static void removeRenderListener(ru.themixray.repeating_mod.RenderListener render) { render_listeners.remove(render); } public void updateButtons() { if (was_build) { - record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); - loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((ru.themixray.repeating_mod.Main.me.is_recording) ? "stop_record" : "start_record"))); + loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((ru.themixray.repeating_mod.Main.me.loop_replay) ? "off_loop" : "on_loop"))); } } @@ -53,7 +53,7 @@ public class RepeatingScreen extends Screen { public void render(DrawContext context, int mouseX, int mouseY, float delta) { renderBackground(context, mouseX, mouseY, delta); - for (RenderListener l : render_listeners) { + for (ru.themixray.repeating_mod.RenderListener l : render_listeners) { if (l.beforeRender()) { l.render(context, mouseX, mouseY, delta); } @@ -61,7 +61,7 @@ public class RepeatingScreen extends Screen { super.render(context, mouseX, mouseY, delta); - for (RenderListener l : render_listeners) { + for (ru.themixray.repeating_mod.RenderListener l : render_listeners) { if (!l.beforeRender()) { l.render(context, mouseX, mouseY, delta); } @@ -70,7 +70,7 @@ public class RepeatingScreen extends Screen { @Override protected void init() { - RecordListWidget list_widget = Main.me.record_list.getWidget(); + RecordListWidget list_widget = ru.themixray.repeating_mod.Main.me.record_list.getWidget(); list_widget.setX(width / 2 + 2); list_widget.setY(height / 2 - list_widget.getHeight() / 2); @@ -79,10 +79,10 @@ public class RepeatingScreen extends Screen { record_btn = ButtonWidget.builder( Text.translatable("text.repeating-mod.start_record"), button -> { - if (!Main.me.is_replaying) { - if (Main.me.is_recording) - Main.me.stopRecording(); - else Main.me.startRecording(); + if (!ru.themixray.repeating_mod.Main.me.is_replaying) { + if (ru.themixray.repeating_mod.Main.me.is_recording) + ru.themixray.repeating_mod.Main.me.stopRecording(); + else ru.themixray.repeating_mod.Main.me.startRecording(); updateButtons(); } }) @@ -91,7 +91,7 @@ public class RepeatingScreen extends Screen { .build(); loop_btn = ButtonWidget.builder(Text.empty(), button -> { - Main.me.loop_replay = !Main.me.loop_replay; + ru.themixray.repeating_mod.Main.me.loop_replay = !ru.themixray.repeating_mod.Main.me.loop_replay; updateButtons(); }) .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) @@ -100,9 +100,9 @@ public class RepeatingScreen extends Screen { pos_delay_slider = new SliderWidget( width / 2 - 120, height / 2 + 12, 120, 20, - (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), - (Main.me.record_pos_delay+1d)/101d) { + (ru.themixray.repeating_mod.Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(ru.themixray.repeating_mod.Main.me.record_pos_delay)), + (ru.themixray.repeating_mod.Main.me.record_pos_delay+1d)/101d) { @Override protected void updateMessage() { @@ -116,9 +116,9 @@ public class RepeatingScreen extends Screen { double v = value*101d-1d; if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - Main.me.record_pos_delay = (long) v; - Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); - Main.me.conf.save(); + ru.themixray.repeating_mod.Main.me.record_pos_delay = (long) v; + ru.themixray.repeating_mod.Main.me.conf.data.put("record_pos_delay",String.valueOf(ru.themixray.repeating_mod.Main.me.record_pos_delay)); + ru.themixray.repeating_mod.Main.me.conf.save(); } @Override @@ -148,7 +148,7 @@ public class RepeatingScreen extends Screen { if (files != null) { for (File file : files) { try { - Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); + ru.themixray.repeating_mod.Main.me.setNowRecord(ru.themixray.repeating_mod.Main.me.record_list.cloneRecord(file)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/themixray/repeating/mod/TickTask.java b/src/main/java/ru/themixray/repeating_mod/TickTask.java similarity index 94% rename from src/main/java/themixray/repeating/mod/TickTask.java rename to src/main/java/ru/themixray/repeating_mod/TickTask.java index c1ff672..d8adbef 100644 --- a/src/main/java/themixray/repeating/mod/TickTask.java +++ b/src/main/java/ru/themixray/repeating_mod/TickTask.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod; +package ru.themixray.repeating_mod; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/themixray/repeating/mod/event/RecordEvent.java b/src/main/java/ru/themixray/repeating_mod/event/RecordEvent.java similarity index 87% rename from src/main/java/themixray/repeating/mod/event/RecordEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/RecordEvent.java index 0c2cb64..c55db10 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/RecordEvent.java @@ -1,6 +1,6 @@ -package themixray.repeating.mod.event; +package ru.themixray.repeating_mod.event; -import themixray.repeating.mod.event.events.*; +import ru.themixray.repeating_mod.event.events.*; public abstract class RecordEvent { public abstract void replay(); diff --git a/src/main/java/themixray/repeating/mod/event/RecordEventType.java b/src/main/java/ru/themixray/repeating_mod/event/RecordEventType.java similarity index 95% rename from src/main/java/themixray/repeating/mod/event/RecordEventType.java rename to src/main/java/ru/themixray/repeating_mod/event/RecordEventType.java index 957159f..853f002 100644 --- a/src/main/java/themixray/repeating/mod/event/RecordEventType.java +++ b/src/main/java/ru/themixray/repeating_mod/event/RecordEventType.java @@ -1,6 +1,6 @@ -package themixray.repeating.mod.event; +package ru.themixray.repeating_mod.event; -import themixray.repeating.mod.event.events.*; +import ru.themixray.repeating_mod.event.events.*; public enum RecordEventType { BLOCK_BREAK('b',"block_break",BlockBreakEvent.class), diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/BlockBreakEvent.java similarity index 85% rename from src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/BlockBreakEvent.java index 0e34fba..dd8a656 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockBreakEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/BlockBreakEvent.java @@ -1,8 +1,8 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; import net.minecraft.util.math.BlockPos; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class BlockBreakEvent extends RecordEvent { public BlockPos pos; diff --git a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java similarity index 92% rename from src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java index bdae075..789ea8f 100644 --- a/src/main/java/themixray/repeating/mod/event/events/BlockInteractEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java @@ -1,12 +1,12 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class BlockInteractEvent extends RecordEvent { public Hand hand; diff --git a/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/DelayEvent.java similarity index 84% rename from src/main/java/themixray/repeating/mod/event/events/DelayEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/DelayEvent.java index 1978702..25dcbac 100644 --- a/src/main/java/themixray/repeating/mod/event/events/DelayEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/DelayEvent.java @@ -1,6 +1,6 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.event.RecordEvent; public class DelayEvent extends RecordEvent { public long delay; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiCharTypeEvent.java similarity index 84% rename from src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiCharTypeEvent.java index 4428984..7c8dae9 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiCharTypeEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiCharTypeEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiCharTypeEvent extends RecordEvent { private char chr; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiCloseEvent.java similarity index 73% rename from src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiCloseEvent.java index f2c3024..38908fb 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiCloseEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiCloseEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiCloseEvent extends RecordEvent { public GuiCloseEvent() {} diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyPressEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyPressEvent.java index cf5221f..0e83969 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiKeyPressEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyPressEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiKeyPressEvent extends RecordEvent { private int keyCode; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyReleaseEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyReleaseEvent.java index 8134c73..253f8bf 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiKeyReleaseEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiKeyReleaseEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiKeyReleaseEvent extends RecordEvent { private int keyCode; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseClickEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseClickEvent.java index 1c47776..6cc2393 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseClickEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseClickEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiMouseClickEvent extends RecordEvent { private double mouseX; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseDragEvent.java similarity index 89% rename from src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseDragEvent.java index f351482..eba566f 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseDragEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseDragEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiMouseDragEvent extends RecordEvent { private double mouseX; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseMoveEvent.java similarity index 84% rename from src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseMoveEvent.java index 7da1c82..94480f2 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseMoveEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseMoveEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiMouseMoveEvent extends RecordEvent { private double mouseX; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseReleaseEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseReleaseEvent.java index 63b8b6b..3e8c1b4 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseReleaseEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseReleaseEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiMouseReleaseEvent extends RecordEvent { private double mouseX; diff --git a/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseScrollEvent.java similarity index 86% rename from src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseScrollEvent.java index 720e027..8df7061 100644 --- a/src/main/java/themixray/repeating/mod/event/events/GuiMouseScrollEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/GuiMouseScrollEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class GuiMouseScrollEvent extends RecordEvent { private double mouseX; diff --git a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java similarity index 98% rename from src/main/java/themixray/repeating/mod/event/events/InputEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java index 9ca3128..74be7c5 100644 --- a/src/main/java/themixray/repeating/mod/event/events/InputEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java @@ -1,7 +1,7 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class InputEvent extends RecordEvent { public Boolean sneaking; diff --git a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java similarity index 90% rename from src/main/java/themixray/repeating/mod/event/events/MoveEvent.java rename to src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java index c8b8bf1..97054ca 100644 --- a/src/main/java/themixray/repeating/mod/event/events/MoveEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java @@ -1,9 +1,9 @@ -package themixray.repeating.mod.event.events; +package ru.themixray.repeating_mod.event.events; import net.minecraft.entity.MovementType; import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.event.RecordEvent; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.RecordEvent; public class MoveEvent extends RecordEvent { public Vec3d vec; diff --git a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/ClientMixin.java similarity index 82% rename from src/main/java/themixray/repeating/mod/mixin/ClientMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/ClientMixin.java index b3adac0..10b1a8e 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ClientMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/ClientMixin.java @@ -1,12 +1,12 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.minecraft.client.MinecraftClient; 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.Main; -import themixray.repeating.mod.TickTask; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.TickTask; @Mixin(MinecraftClient.class) public abstract class ClientMixin { diff --git a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java similarity index 88% rename from src/main/java/themixray/repeating/mod/mixin/EntityMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java index 185de64..9bf8341 100644 --- a/src/main/java/themixray/repeating/mod/mixin/EntityMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.minecraft.entity.Entity; import org.spongepowered.asm.mixin.Mixin; @@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import themixray.repeating.mod.Main; +import ru.themixray.repeating_mod.Main; import java.util.UUID; diff --git a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java similarity index 84% rename from src/main/java/themixray/repeating/mod/mixin/InputMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java index c58f53c..33acbf1 100644 --- a/src/main/java/themixray/repeating/mod/mixin/InputMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java @@ -1,11 +1,11 @@ -package themixray.repeating.mod.mixin; +package ru.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.Main; +import ru.themixray.repeating_mod.Main; @Mixin(KeyboardInput.class) public abstract class InputMixin { diff --git a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/MovementMixin.java similarity index 82% rename from src/main/java/themixray/repeating/mod/mixin/MovementMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/MovementMixin.java index f9084e7..73f970b 100644 --- a/src/main/java/themixray/repeating/mod/mixin/MovementMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/MovementMixin.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; @@ -9,10 +9,10 @@ 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.Main; -import themixray.repeating.mod.event.events.BlockBreakEvent; -import themixray.repeating.mod.event.events.BlockInteractEvent; -import themixray.repeating.mod.TickTask; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.events.BlockBreakEvent; +import ru.themixray.repeating_mod.event.events.BlockInteractEvent; +import ru.themixray.repeating_mod.TickTask; @Mixin(ClientPlayerEntity.class) public abstract class MovementMixin { diff --git a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/NetworkMixin.java similarity index 94% rename from src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/NetworkMixin.java index 396b6c2..58f7570 100644 --- a/src/main/java/themixray/repeating/mod/mixin/NetworkMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/NetworkMixin.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.listener.ServerPlayPacketListener; diff --git a/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/PlayerMixin.java similarity index 64% rename from src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/PlayerMixin.java index 67735f9..c5627c8 100644 --- a/src/main/java/themixray/repeating/mod/mixin/PlayerMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/PlayerMixin.java @@ -1,18 +1,12 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.entity.Entity; import net.minecraft.network.ClientConnection; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import themixray.repeating.mod.Main; - -import java.util.UUID; +import ru.themixray.repeating_mod.Main; @Mixin(ClientConnection.class) public abstract class PlayerMixin { diff --git a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/RendererMixin.java similarity index 85% rename from src/main/java/themixray/repeating/mod/mixin/RendererMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/RendererMixin.java index 22ca225..c035167 100644 --- a/src/main/java/themixray/repeating/mod/mixin/RendererMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/RendererMixin.java @@ -1,11 +1,11 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.minecraft.client.render.*; 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.TickTask; +import ru.themixray.repeating_mod.TickTask; @Mixin(GameRenderer.class) public abstract class RendererMixin { diff --git a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/ScreenMixin.java similarity index 90% rename from src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java rename to src/main/java/ru/themixray/repeating_mod/mixin/ScreenMixin.java index 63d5fc5..3d74704 100644 --- a/src/main/java/themixray/repeating/mod/mixin/ScreenMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/ScreenMixin.java @@ -1,20 +1,15 @@ -package themixray.repeating.mod.mixin; +package ru.themixray.repeating_mod.mixin; import net.minecraft.client.gui.AbstractParentElement; import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.ParentElement; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.render.GameRenderer; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.TickTask; -import themixray.repeating.mod.event.events.*; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.event.events.*; @Mixin(Screen.class) public abstract class ScreenMixin extends AbstractParentElement implements Drawable { diff --git a/src/main/java/themixray/repeating/mod/render/RenderHelper.java b/src/main/java/ru/themixray/repeating_mod/render/RenderHelper.java similarity index 87% rename from src/main/java/themixray/repeating/mod/render/RenderHelper.java rename to src/main/java/ru/themixray/repeating_mod/render/RenderHelper.java index 4f80464..c67abd4 100644 --- a/src/main/java/themixray/repeating/mod/render/RenderHelper.java +++ b/src/main/java/ru/themixray/repeating_mod/render/RenderHelper.java @@ -1,10 +1,10 @@ -package themixray.repeating.mod.render; +package ru.themixray.repeating_mod.render; import lombok.experimental.UtilityClass; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.util.math.Vec3d; -import themixray.repeating.mod.render.buffer.WorldBuffer; -import themixray.repeating.mod.render.shader.ShaderManager; +import org.joml.Vector3f; +import ru.themixray.repeating_mod.render.buffer.WorldBuffer; +import ru.themixray.repeating_mod.render.shader.ShaderManager; import java.awt.*; @@ -27,8 +27,8 @@ public class RenderHelper { } public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) { - buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(new Vector3f(x1, y1, z1), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(new Vector3f(x2, y2, z2), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); } public static WorldBuffer startTri(WorldRenderContext context) { @@ -49,9 +49,9 @@ public class RenderHelper { } public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) { - buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); - buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(new Vector3f(x1, y1, z1), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(new Vector3f(x2, y2, z2), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); + buffer.vert(new Vector3f(x3, y3, z3), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); } public static void drawRectFromTri(WorldBuffer buffer, diff --git a/src/main/java/themixray/repeating/mod/render/RenderSystem.java b/src/main/java/ru/themixray/repeating_mod/render/RenderSystem.java similarity index 52% rename from src/main/java/themixray/repeating/mod/render/RenderSystem.java rename to src/main/java/ru/themixray/repeating_mod/render/RenderSystem.java index a5fa15c..90f12e9 100644 --- a/src/main/java/themixray/repeating/mod/render/RenderSystem.java +++ b/src/main/java/ru/themixray/repeating_mod/render/RenderSystem.java @@ -1,8 +1,8 @@ -package themixray.repeating.mod.render; +package ru.themixray.repeating_mod.render; import lombok.experimental.UtilityClass; -import themixray.repeating.mod.render.buffer.BufferManager; -import themixray.repeating.mod.render.shader.ShaderManager; +import ru.themixray.repeating_mod.render.buffer.BufferManager; +import ru.themixray.repeating_mod.render.shader.ShaderManager; @UtilityClass public class RenderSystem { diff --git a/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java b/src/main/java/ru/themixray/repeating_mod/render/buffer/BufferManager.java similarity index 95% rename from src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java rename to src/main/java/ru/themixray/repeating_mod/render/buffer/BufferManager.java index 76d2853..9fb32ff 100644 --- a/src/main/java/themixray/repeating/mod/render/buffer/BufferManager.java +++ b/src/main/java/ru/themixray/repeating_mod/render/buffer/BufferManager.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.render.buffer; +package ru.themixray.repeating_mod.render.buffer; import lombok.experimental.UtilityClass; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; diff --git a/src/main/java/themixray/repeating/mod/render/buffer/Vertex.java b/src/main/java/ru/themixray/repeating_mod/render/buffer/Vertex.java similarity index 50% rename from src/main/java/themixray/repeating/mod/render/buffer/Vertex.java rename to src/main/java/ru/themixray/repeating_mod/render/buffer/Vertex.java index 5aaa113..a87e2e6 100644 --- a/src/main/java/themixray/repeating/mod/render/buffer/Vertex.java +++ b/src/main/java/ru/themixray/repeating_mod/render/buffer/Vertex.java @@ -1,14 +1,11 @@ -package themixray.repeating.mod.render.buffer; +package ru.themixray.repeating_mod.render.buffer; import lombok.Getter; +import org.joml.Vector3f; public class Vertex { @Getter - private float x; - @Getter - private float y; - @Getter - private float z; + private Vector3f pos; @Getter private float r; @Getter @@ -18,10 +15,8 @@ public class Vertex { @Getter private float a; - public Vertex(float x, float y, float z, float r, float g, float b, float a) { - this.x = x; - this.y = y; - this.z = z; + public Vertex(Vector3f pos, float r, float g, float b, float a) { + this.pos = pos; this.r = r; this.g = g; this.b = b; diff --git a/src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java b/src/main/java/ru/themixray/repeating_mod/render/buffer/WorldBuffer.java similarity index 63% rename from src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java rename to src/main/java/ru/themixray/repeating_mod/render/buffer/WorldBuffer.java index 390f97d..fa27670 100644 --- a/src/main/java/themixray/repeating/mod/render/buffer/WorldBuffer.java +++ b/src/main/java/ru/themixray/repeating_mod/render/buffer/WorldBuffer.java @@ -1,11 +1,12 @@ -package themixray.repeating.mod.render.buffer; +package ru.themixray.repeating_mod.render.buffer; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.util.math.Vec3d; import org.apache.commons.lang3.ArrayUtils; import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; import org.lwjgl.BufferUtils; -import themixray.repeating.mod.render.shader.Shader; +import ru.themixray.repeating_mod.render.shader.Shader; import java.nio.FloatBuffer; import java.util.ArrayList; @@ -18,17 +19,19 @@ public class WorldBuffer { private final int drawMode; private final Shader shader; private FloatBuffer projectionMatrix; - private final Vec3d cameraPos; + private final Vector3f cameraPos; + private final Quaternionf cameraRot; public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) { this.drawMode = drawMode; this.shader = shader; - this.cameraPos = worldRenderContext.camera().getPos(); - makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix()); + this.cameraPos = worldRenderContext.camera().getPos().toVector3f(); + this.cameraRot = worldRenderContext.camera().getRotation().invert(); + this.projectionMatrix = worldRenderContext.projectionMatrix().mul(worldRenderContext.matrixStack().peek().getPositionMatrix()).get(BufferUtils.createFloatBuffer(16)); } - public void vert(float x, float y, float z, float r, float g, float b, float a) { - vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a)); + public void vert(Vector3f pos, float r, float g, float b, float a) { + vertices.add(new Vertex(cameraRot.transform(pos.sub(cameraPos)), r, g, b, a)); } public void draw() { @@ -36,7 +39,7 @@ public class WorldBuffer { BufferManager.bindBuffer(); BufferManager.writeBuffer(getBuffer()); - applyProjectionMatrix(); + shader.uniformMatrix4f("u_projection", projectionMatrix); glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(0); @@ -55,12 +58,12 @@ public class WorldBuffer { private FloatBuffer getBuffer() { FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7); ArrayList floats = new ArrayList<>(); - for (Vertex vertex : vertices) { - floats.add(vertex.getX()); - floats.add(vertex.getY()); - floats.add(vertex.getZ()); + for (ru.themixray.repeating_mod.render.buffer.Vertex vertex : vertices) { + floats.add(vertex.getPos().x); + floats.add(vertex.getPos().y); + floats.add(vertex.getPos().z); } - for (Vertex vertex : vertices) { + for (ru.themixray.repeating_mod.render.buffer.Vertex vertex : vertices) { floats.add(vertex.getR()); floats.add(vertex.getG()); floats.add(vertex.getB()); @@ -71,12 +74,4 @@ public class WorldBuffer { floatBuffer.put(ArrayUtils.toPrimitive(floatArray)); return floatBuffer.flip(); } - - private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) { - this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16)); - } - - private void applyProjectionMatrix() { - shader.uniformMatrix4f("u_projection", projectionMatrix); - } } diff --git a/src/main/java/themixray/repeating/mod/render/shader/Shader.java b/src/main/java/ru/themixray/repeating_mod/render/shader/Shader.java similarity index 67% rename from src/main/java/themixray/repeating/mod/render/shader/Shader.java rename to src/main/java/ru/themixray/repeating_mod/render/shader/Shader.java index e11dd9a..efe5550 100644 --- a/src/main/java/themixray/repeating/mod/render/shader/Shader.java +++ b/src/main/java/ru/themixray/repeating_mod/render/shader/Shader.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.render.shader; +package ru.themixray.repeating_mod.render.shader; import lombok.Getter; @@ -12,8 +12,8 @@ public class Shader { public Shader(String name) { - int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX); - int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT); + int v = ru.themixray.repeating_mod.render.shader.ShaderManager.loadShaderProgram(name, ru.themixray.repeating_mod.render.shader.ShaderManager.ShaderType.VERTEX); + int f = ru.themixray.repeating_mod.render.shader.ShaderManager.loadShaderProgram(name, ru.themixray.repeating_mod.render.shader.ShaderManager.ShaderType.FRAGMENT); this.id = glCreateProgram(); glAttachShader(id, v); glAttachShader(id, f); diff --git a/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java similarity index 93% rename from src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java rename to src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java index 08d6a9b..34c0d25 100644 --- a/src/main/java/themixray/repeating/mod/render/shader/ShaderManager.java +++ b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java @@ -1,4 +1,4 @@ -package themixray.repeating.mod.render.shader; +package ru.themixray.repeating_mod.render.shader; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.TextureUtil; @@ -27,14 +27,14 @@ import static org.lwjgl.opengl.GL33.*; @UtilityClass public class ShaderManager { @Getter - private Shader positionColorShader; + private ru.themixray.repeating_mod.render.shader.Shader positionColorShader; public void init() { ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders()); } private void loadShaders() { - positionColorShader = new Shader("position_color"); + positionColorShader = new ru.themixray.repeating_mod.render.shader.Shader("position_color"); } public int loadShaderProgram(String name, ShaderType type) { diff --git a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java similarity index 89% rename from src/main/java/themixray/repeating/mod/widget/RecordListWidget.java rename to src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java index d038f91..5b7a7e3 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordListWidget.java +++ b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java @@ -1,22 +1,14 @@ -package themixray.repeating.mod.widget; +package ru.themixray.repeating_mod.widget; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.*; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.RecordState; -import themixray.repeating.mod.RepeatingScreen; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.RecordState; +import ru.themixray.repeating_mod.RepeatingScreen; -import java.util.ArrayList; import java.util.LinkedList; -import java.util.List; -import java.util.function.Consumer; public class RecordListWidget extends ScrollableWidget { private LinkedList widgets = new LinkedList<>(); diff --git a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java b/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java similarity index 96% rename from src/main/java/themixray/repeating/mod/widget/RecordWidget.java rename to src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java index 719a606..7d53358 100644 --- a/src/main/java/themixray/repeating/mod/widget/RecordWidget.java +++ b/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java @@ -1,17 +1,14 @@ -package themixray.repeating.mod.widget; +package ru.themixray.repeating_mod.widget; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Drawable; import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.*; -import net.minecraft.text.Style; import net.minecraft.text.Text; -import net.minecraft.text.TextColor; import net.minecraft.util.Formatting; -import themixray.repeating.mod.Main; -import themixray.repeating.mod.RecordState; -import themixray.repeating.mod.RenderListener; -import themixray.repeating.mod.RepeatingScreen; +import ru.themixray.repeating_mod.Main; +import ru.themixray.repeating_mod.RecordState; +import ru.themixray.repeating_mod.RepeatingScreen; import java.awt.*; import java.io.IOException; diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 2e09cc1..9bbbbe2 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -19,7 +19,7 @@ "environment": "client", "entrypoints": { "client": [ - "themixray.repeating.mod.Main" + "ru.themixray.repeating_mod.Main" ] }, "mixins": [ diff --git a/src/main/resources/repeating-mod.mixins.json b/src/main/resources/repeating-mod.mixins.json index 4196f7e..7bfbaa8 100644 --- a/src/main/resources/repeating-mod.mixins.json +++ b/src/main/resources/repeating-mod.mixins.json @@ -1,7 +1,7 @@ { "required": true, "minVersion": "0.8", - "package": "themixray.repeating.mod.mixin", + "package": "ru.themixray.repeating_mod.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ ], From 2944395e54a21aecfad847b59cf67f1c30b6baa9 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 05:09:39 +0300 Subject: [PATCH 50/72] remove some crap --- .../java/ru/themixray/repeating_mod/Main.java | 28 ++++++------- .../repeating_mod/RepeatingScreen.java | 40 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/main/java/ru/themixray/repeating_mod/Main.java b/src/main/java/ru/themixray/repeating_mod/Main.java index e5ac5a9..3079d38 100644 --- a/src/main/java/ru/themixray/repeating_mod/Main.java +++ b/src/main/java/ru/themixray/repeating_mod/Main.java @@ -35,21 +35,21 @@ public class Main implements ClientModInitializer { public static final FabricLoader loader = FabricLoader.getInstance(); public static Main me; - public ru.themixray.repeating_mod.RecordList record_list; - public ru.themixray.repeating_mod.RecordState now_record; + public RecordList record_list; + public RecordState now_record; public boolean is_recording = false; public long last_record = -1; - public ru.themixray.repeating_mod.TickTask move_tick = null; + public TickTask move_tick = null; - public ru.themixray.repeating_mod.TickTask replay_tick = null; + public TickTask replay_tick = null; public boolean is_replaying = false; public boolean loop_replay = false; public static InputEvent input_replay = null; public long living_ticks = 0; - public static ru.themixray.repeating_mod.RepeatingScreen menu; + public static RepeatingScreen menu; private static KeyBinding menu_key; private static KeyBinding toggle_replay_key; private static KeyBinding toggle_record_key; @@ -58,7 +58,7 @@ public class Main implements ClientModInitializer { public static Random rand = new Random(); - public ru.themixray.repeating_mod.EasyConfig conf; + public EasyConfig conf; public File records_folder; @Override @@ -71,7 +71,7 @@ public class Main implements ClientModInitializer { records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); if (!records_folder.exists()) records_folder.mkdir(); - record_list = new ru.themixray.repeating_mod.RecordList(records_folder); + record_list = new RecordList(records_folder); record_list.loadRecords(); RenderSystem.init(); @@ -88,13 +88,13 @@ public class Main implements ClientModInitializer { }); ClientTickEvents.END_CLIENT_TICK.register(client -> { - ru.themixray.repeating_mod.TickTask.tickTasks(ru.themixray.repeating_mod.TickTask.TickAt.CLIENT_EVENT); + TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); }); Map def = new HashMap<>(); def.put("record_pos_delay", String.valueOf(record_pos_delay)); - conf = new ru.themixray.repeating_mod.EasyConfig(loader.getConfigDir(),"repeating-mod",def); + conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def); record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); @@ -108,7 +108,7 @@ public class Main implements ClientModInitializer { "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, -1,"text.repeating-mod.name")); - menu = new ru.themixray.repeating_mod.RepeatingScreen(); + menu = new RepeatingScreen(); ClientTickEvents.END_CLIENT_TICK.register(client -> { if (menu_key.wasPressed()) client.setScreen(menu); @@ -132,7 +132,7 @@ public class Main implements ClientModInitializer { } }); - new ru.themixray.repeating_mod.TickTask(0,0) { + new TickTask(0,0) { @Override public void run() { living_ticks++; @@ -142,7 +142,7 @@ public class Main implements ClientModInitializer { System.setProperty("java.awt.headless", "false"); } - public void setNowRecord(ru.themixray.repeating_mod.RecordState record) { + public void setNowRecord(RecordState record) { now_record = record; } @@ -177,7 +177,7 @@ public class Main implements ClientModInitializer { now_record.setStartRecordPos(start_pos); if (record_pos_delay > 0) { - move_tick = new ru.themixray.repeating_mod.TickTask( + move_tick = new TickTask( record_pos_delay, record_pos_delay) { @Override @@ -277,7 +277,7 @@ public class Main implements ClientModInitializer { List events = now_record.getEvents(); - replay_tick = new ru.themixray.repeating_mod.TickTask(0,0, ru.themixray.repeating_mod.TickTask.TickAt.CLIENT_EVENT) { + replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { public int replay_index = 0; @Override diff --git a/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java index 6f4df2c..8d95918 100644 --- a/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java +++ b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java @@ -20,7 +20,7 @@ import java.util.List; @Environment(EnvType.CLIENT) public class RepeatingScreen extends Screen { - private static List render_listeners = new ArrayList<>(); + private static List render_listeners = new ArrayList<>(); public ButtonWidget record_btn; public ButtonWidget loop_btn; @@ -34,18 +34,18 @@ public class RepeatingScreen extends Screen { super(Text.empty()); } - public static void addRenderListener(ru.themixray.repeating_mod.RenderListener render) { + public static void addRenderListener(RenderListener render) { render_listeners.add(render); } - public static void removeRenderListener(ru.themixray.repeating_mod.RenderListener render) { + public static void removeRenderListener(RenderListener render) { render_listeners.remove(render); } public void updateButtons() { if (was_build) { - record_btn.setMessage(Text.translatable("text.repeating-mod." + ((ru.themixray.repeating_mod.Main.me.is_recording) ? "stop_record" : "start_record"))); - loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((ru.themixray.repeating_mod.Main.me.loop_replay) ? "off_loop" : "on_loop"))); + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); + loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop"))); } } @@ -53,7 +53,7 @@ public class RepeatingScreen extends Screen { public void render(DrawContext context, int mouseX, int mouseY, float delta) { renderBackground(context, mouseX, mouseY, delta); - for (ru.themixray.repeating_mod.RenderListener l : render_listeners) { + for (RenderListener l : render_listeners) { if (l.beforeRender()) { l.render(context, mouseX, mouseY, delta); } @@ -61,7 +61,7 @@ public class RepeatingScreen extends Screen { super.render(context, mouseX, mouseY, delta); - for (ru.themixray.repeating_mod.RenderListener l : render_listeners) { + for (RenderListener l : render_listeners) { if (!l.beforeRender()) { l.render(context, mouseX, mouseY, delta); } @@ -70,7 +70,7 @@ public class RepeatingScreen extends Screen { @Override protected void init() { - RecordListWidget list_widget = ru.themixray.repeating_mod.Main.me.record_list.getWidget(); + RecordListWidget list_widget = Main.me.record_list.getWidget(); list_widget.setX(width / 2 + 2); list_widget.setY(height / 2 - list_widget.getHeight() / 2); @@ -79,10 +79,10 @@ public class RepeatingScreen extends Screen { record_btn = ButtonWidget.builder( Text.translatable("text.repeating-mod.start_record"), button -> { - if (!ru.themixray.repeating_mod.Main.me.is_replaying) { - if (ru.themixray.repeating_mod.Main.me.is_recording) - ru.themixray.repeating_mod.Main.me.stopRecording(); - else ru.themixray.repeating_mod.Main.me.startRecording(); + if (!Main.me.is_replaying) { + if (Main.me.is_recording) + Main.me.stopRecording(); + else Main.me.startRecording(); updateButtons(); } }) @@ -91,7 +91,7 @@ public class RepeatingScreen extends Screen { .build(); loop_btn = ButtonWidget.builder(Text.empty(), button -> { - ru.themixray.repeating_mod.Main.me.loop_replay = !ru.themixray.repeating_mod.Main.me.loop_replay; + Main.me.loop_replay = !Main.me.loop_replay; updateButtons(); }) .dimensions(width / 2 - 120, height / 2 - 10, 120, 20) @@ -100,9 +100,9 @@ public class RepeatingScreen extends Screen { pos_delay_slider = new SliderWidget( width / 2 - 120, height / 2 + 12, 120, 20, - (ru.themixray.repeating_mod.Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : - Text.translatable("text.repeating-mod.pos_delay", String.valueOf(ru.themixray.repeating_mod.Main.me.record_pos_delay)), - (ru.themixray.repeating_mod.Main.me.record_pos_delay+1d)/101d) { + (Main.me.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : + Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)), + (Main.me.record_pos_delay+1d)/101d) { @Override protected void updateMessage() { @@ -116,9 +116,9 @@ public class RepeatingScreen extends Screen { double v = value*101d-1d; if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v))); - ru.themixray.repeating_mod.Main.me.record_pos_delay = (long) v; - ru.themixray.repeating_mod.Main.me.conf.data.put("record_pos_delay",String.valueOf(ru.themixray.repeating_mod.Main.me.record_pos_delay)); - ru.themixray.repeating_mod.Main.me.conf.save(); + Main.me.record_pos_delay = (long) v; + Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay)); + Main.me.conf.save(); } @Override @@ -148,7 +148,7 @@ public class RepeatingScreen extends Screen { if (files != null) { for (File file : files) { try { - ru.themixray.repeating_mod.Main.me.setNowRecord(ru.themixray.repeating_mod.Main.me.record_list.cloneRecord(file)); + Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); } catch (Exception e) { throw new RuntimeException(e); } From 759b6cb05ec6309c6eaa61bd449e9a9c2999e335 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 05:17:53 +0300 Subject: [PATCH 51/72] gitea action --- .gitea/workflows/java.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .gitea/workflows/java.yml diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml new file mode 100644 index 0000000..77f293a --- /dev/null +++ b/.gitea/workflows/java.yml @@ -0,0 +1,35 @@ +name: Build fabric mod + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle 8.12.1 + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: '8.12.1' + + - name: Build with Gradle Wrapper + run: ./gradlew build + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: build + path: build/libs \ No newline at end of file From f49b1e21a314226fa4b7475135ccc15d309e74ea Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 05:23:49 +0300 Subject: [PATCH 52/72] maybefix gitea actions --- .gitea/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index 77f293a..df59382 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -21,7 +21,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle 8.12.1 - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 with: gradle-version: '8.12.1' From 1e4f4fdaeb3368379d6c0386f0314171139d6251 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 06:00:20 +0300 Subject: [PATCH 53/72] gitea actions fix x3 --- .gitea/workflows/java.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index df59382..377de88 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -9,9 +9,6 @@ on: jobs: build: runs-on: ubuntu-latest - permissions: - contents: read - steps: - uses: actions/checkout@v4 - name: Set up JDK 21 From b6084063a1efcd6d231052a7c6b21a0e32abb66c Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 06:23:35 +0300 Subject: [PATCH 54/72] make gradlew exec --- .gitea/workflows/java.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index 377de88..1862a70 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -22,6 +22,9 @@ jobs: with: gradle-version: '8.12.1' + - name: Make gradlew executable + run: chmod +x ./gradlew + - name: Build with Gradle Wrapper run: ./gradlew build From 62dcb75960345a3ee1119a078e0b7749682984b7 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 06:48:00 +0300 Subject: [PATCH 55/72] fix upload artifact --- .gitea/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index 1862a70..b3d122f 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -32,4 +32,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: build - path: build/libs \ No newline at end of file + path: build/libs/* \ No newline at end of file From 2e62b69a218db43bd756b19fdd7f5aa6932c6ea1 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 07:45:08 +0300 Subject: [PATCH 56/72] action refactor --- .gitea/workflows/java.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index b3d122f..d0cc80b 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -1,16 +1,13 @@ -name: Build fabric mod +on: [push, pull_request] -on: - push: - branches: [ '*' ] - pull_request: - branches: [ '*' ] +name: Build fabric mod jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: @@ -22,14 +19,10 @@ jobs: with: gradle-version: '8.12.1' - - name: Make gradlew executable - run: chmod +x ./gradlew - - name: Build with Gradle Wrapper - run: ./gradlew build + run: chmod +x ./gradlew; ./gradlew build - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: build path: build/libs/* \ No newline at end of file From d4abb39c8012984db9c640bc15b9642468582357 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 08:01:57 +0300 Subject: [PATCH 57/72] downgrade upload-artifact --- .gitea/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index d0cc80b..8fc764a 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -23,6 +23,6 @@ jobs: run: chmod +x ./gradlew; ./gradlew build - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: path: build/libs/* \ No newline at end of file From 63132684cd2ed70d355282f49ba45e64a8dee64e Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 19:28:24 +0300 Subject: [PATCH 58/72] Update README.md --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 43b4faa..e0fa54f 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,16 @@ This mod can record your movements and play them back. Simply start recording, perform any actions, stop the recording, return to the starting point, and then you can replay everything you did. -## Showcase +### Showcase ![Preview gif](preview.gif) -## Controls +This is how menu looks like: + +![repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/4123068f-b150-45ae-8ae3-fcaa0e6bb9f8) + + +### Controls Default Repeating Mod keys @@ -20,14 +25,15 @@ Toggle recording | not specified by default Toggle replay | not specified by default ``` -## Menu - -This is how menu looks like: - -![repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/4123068f-b150-45ae-8ae3-fcaa0e6bb9f8) - ## Roadmap - [ ] relative mode for repeating actions (like mining) - [ ] record mouse and keyboard in gui - [ ] practice mode (like in geometry dash but for parkours) + +### Contributing + +If you would like to contribute to the project, feel free to fork the repository and submit a pull request. + +### License +This project is licensed under the WTFPL License From 666377e5ee261669fc68724e6913118388641a3d Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 20:04:53 +0300 Subject: [PATCH 59/72] fix pos event --- .gitea/workflows/java.yml | 1 + src/main/java/ru/themixray/repeating_mod/Main.java | 6 +++--- .../repeating_mod/event/events/MoveEvent.java | 12 +++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index 8fc764a..2dd136f 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -25,4 +25,5 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v3 with: + name: build path: build/libs/* \ No newline at end of file diff --git a/src/main/java/ru/themixray/repeating_mod/Main.java b/src/main/java/ru/themixray/repeating_mod/Main.java index 3079d38..17cf367 100644 --- a/src/main/java/ru/themixray/repeating_mod/Main.java +++ b/src/main/java/ru/themixray/repeating_mod/Main.java @@ -54,7 +54,7 @@ public class Main implements ClientModInitializer { private static KeyBinding toggle_replay_key; private static KeyBinding toggle_record_key; - public long record_pos_delay = 20; + public long record_pos_delay = -1; public static Random rand = new Random(); @@ -173,7 +173,7 @@ public class Main implements ClientModInitializer { now_record = record_list.newRecord(); Vec3d start_pos = client.player.getPos(); - now_record.addEvent(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); + recordTick(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch())); now_record.setStartRecordPos(start_pos); if (record_pos_delay > 0) { @@ -182,7 +182,7 @@ public class Main implements ClientModInitializer { record_pos_delay) { @Override public void run() { - now_record.addEvent(new MoveEvent(client.player.getPos(), + recordTick(new MoveEvent(client.player.getPos(), client.player.getHeadYaw(), client.player.getPitch())); } }; diff --git a/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java index 97054ca..9506eae 100644 --- a/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/MoveEvent.java @@ -10,6 +10,8 @@ public class MoveEvent extends RecordEvent { public float yaw; public float pitch; + public static final float MOVE_THRESHOLD = 0.001f; + public static MoveEvent deserialize(String[] a) { return new MoveEvent(new Vec3d( Double.parseDouble(a[0]), @@ -29,9 +31,13 @@ public class MoveEvent extends RecordEvent { if (Main.client.player != null) { Vec3d p = Main.client.player.getPos(); Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ()); - Main.client.player.move(MovementType.SELF, v); - Main.client.player.setYaw(yaw); - Main.client.player.setPitch(pitch); + if (Math.abs(v.x) > MOVE_THRESHOLD || Math.abs(v.y) > MOVE_THRESHOLD || Math.abs(v.z) > MOVE_THRESHOLD) + Main.client.player.move(MovementType.SELF, v); + + if (Math.abs(Main.client.player.getYaw() - yaw) > MOVE_THRESHOLD) + Main.client.player.setYaw(yaw); + if (Math.abs(Main.client.player.getPitch() - pitch) > MOVE_THRESHOLD) + Main.client.player.setPitch(pitch); } } From 5d5e628cdf41ae0532ffac25a44fea7b787386d7 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 20:18:04 +0300 Subject: [PATCH 60/72] add dev artifacts --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index e0fa54f..c0a9513 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,16 @@ Toggle recording | not specified by default Toggle replay | not specified by default ``` +## Where to download + +### Stable releases + +You can find stable releases on [Modrinth](https://modrinth.com/mod/repeating-mod/versions) or on [Releases page](https://git.meex.lol/MeexReay/repeating_mod/releases) + +### Developement artifacts + +Artifacts are automatically built with [Gitea Actions](https://git.meex.lol/MeexReay/repeating_mod/actions) on each commit + ## Roadmap - [ ] relative mode for repeating actions (like mining) From 38127eb69f2ff81b3ba84791f28837e835e7f130 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 20:35:23 +0300 Subject: [PATCH 61/72] make img folder for readme images --- README.md | 4 ++-- img/menu.png | Bin 0 -> 135226 bytes preview.gif => img/preview.gif | Bin 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 img/menu.png rename preview.gif => img/preview.gif (100%) diff --git a/README.md b/README.md index c0a9513..d8959df 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ Simply start recording, perform any actions, stop the recording, return to the s ### Showcase -![Preview gif](preview.gif) +![Preview gif](img/preview.gif) This is how menu looks like: -![repeating mod menu](https://github.com/MeexReay/repeating-mod/assets/127148610/4123068f-b150-45ae-8ae3-fcaa0e6bb9f8) +![repeating mod menu](img/menu.png) ### Controls diff --git a/img/menu.png b/img/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf79282973bd1efb06d669db04a221864266cee GIT binary patch literal 135226 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 HP6 Date: Sat, 14 Jun 2025 21:38:30 +0300 Subject: [PATCH 62/72] fix gitea actions --- .gitea/workflows/java.yml | 2 +- README.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/java.yml b/.gitea/workflows/java.yml index 2dd136f..5c47e28 100644 --- a/.gitea/workflows/java.yml +++ b/.gitea/workflows/java.yml @@ -1,4 +1,4 @@ -on: [push, pull_request] +on: [ push ] name: Build fabric mod diff --git a/README.md b/README.md index d8959df..ca695e9 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,10 @@ Toggle replay | not specified by default You can find stable releases on [Modrinth](https://modrinth.com/mod/repeating-mod/versions) or on [Releases page](https://git.meex.lol/MeexReay/repeating_mod/releases) -### Developement artifacts +### Development artifacts -Artifacts are automatically built with [Gitea Actions](https://git.meex.lol/MeexReay/repeating_mod/actions) on each commit +Artifacts are automatically built with [Gitea Actions](https://git.meex.lol/MeexReay/repeating_mod/actions) on each commit. \ +[Download latest artifact](https://git.meex.lol/MeexReay/repeating_mod/actions/runs/latest/artifacts/build) ## Roadmap From 4714b1dbfbe09f03ed064970ba5cf596e83b5687 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 22:22:27 +0300 Subject: [PATCH 63/72] add compatible with comment --- gradle.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 489444b..49aab6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ fabric_version=0.102.0+1.21 # Mod Properties mod_version = 1.1.2+1.21 maven_group = ru.themixray -archives_base_name = repeating-mod \ No newline at end of file +archives_base_name = repeating-mod + +# Compatible with: 1.21, 1.21.1 \ No newline at end of file From 412c3f47ab3fa025d0750d6e5f3075c5fa8c2688 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 14 Jun 2025 23:46:18 +0300 Subject: [PATCH 64/72] port to 1.21.2, MOVEMENTS ARE BROKEN --- gradle.properties | 8 +- .../java/ru/themixray/repeating_mod/Main.java | 54 +---- .../event/events/InputEvent.java | 208 ++++++++++-------- .../repeating_mod/mixin/EntityMixin.java | 4 +- .../render/shader/ShaderManager.java | 2 +- 5 files changed, 131 insertions(+), 145 deletions(-) diff --git a/gradle.properties b/gradle.properties index 49aab6b..bcf329b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,16 +4,16 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21 -yarn_mappings=1.21+build.9 +minecraft_version=1.21.2 +yarn_mappings=1.21.2+build.1 loader_version=0.16.14 loom_version=1.10-SNAPSHOT # Fabric API -fabric_version=0.102.0+1.21 +fabric_version=0.106.1+1.21.2 # Mod Properties -mod_version = 1.1.2+1.21 +mod_version = 1.1.2+1.21.2 maven_group = ru.themixray archives_base_name = repeating-mod diff --git a/src/main/java/ru/themixray/repeating_mod/Main.java b/src/main/java/ru/themixray/repeating_mod/Main.java index 17cf367..296904c 100644 --- a/src/main/java/ru/themixray/repeating_mod/Main.java +++ b/src/main/java/ru/themixray/repeating_mod/Main.java @@ -209,46 +209,14 @@ public class Main implements ClientModInitializer { return; } - InputEvent l = ((InputEvent) now_record.getLastEvent("input")); - if (l == null) { - InputEvent e = new InputEvent( - client.player.input.sneaking, - client.player.input.jumping, - client.player.input.movementSideways, - client.player.input.movementForward, - client.player.input.pressingForward, - client.player.input.pressingBack, - client.player.input.pressingLeft, - client.player.input.pressingRight, - client.player.getHeadYaw(), - client.player.getBodyYaw(), - client.player.getPitch(), - client.player.isSprinting(), - client.player.getYaw(), - client.player.getMovementSpeed()); - recordTick(e); - } else { - InputEvent e = new InputEvent( - ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, - ((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, - (((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways, - (((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, - ((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, - ((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, - ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, - ((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, - client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), - ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), - client.player.getYaw(),client.player.getMovementSpeed()); + InputEvent curr = InputEvent.current(); + if (curr == null) return; - if (!(e.isEmpty() && - e.yaw == l.yaw && - e.head_yaw == l.head_yaw && - e.pitch == l.pitch && - e.body_yaw == l.body_yaw)) { - e.fillEmpty(l); - recordTick(e); - } + InputEvent last = ((InputEvent) now_record.getLastEvent("input")); + if (last == null) { + recordTick(curr); + } else if (!curr.equals(last)) { + recordTick(curr.differs(last)); } } @@ -332,10 +300,10 @@ public class Main implements ClientModInitializer { client.player.sendMessage(Text.literal("[") .append(Text.translatable("text.repeating-mod.name")) .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) - .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); + .append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY)), false); } - public static void sendDebug(String s) { - client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s))); - } +// public static void sendDebug(String s) { +// client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s)), false); +// } } diff --git a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java index 74be7c5..e0ce865 100644 --- a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java @@ -1,19 +1,17 @@ package ru.themixray.repeating_mod.event.events; +import net.minecraft.util.PlayerInput; import ru.themixray.repeating_mod.Main; import ru.themixray.repeating_mod.event.RecordEvent; public class InputEvent extends RecordEvent { - public Boolean sneaking; - public Boolean jumping; - public Boolean pressingForward; - public Boolean pressingBack; - public Boolean pressingLeft; - public Boolean pressingRight; - public Boolean sprinting; - - public Float movementSideways; - public Float movementForward; + public Boolean forward; + public Boolean backward; + public Boolean left; + public Boolean right; + public Boolean jump; + public Boolean sneak; + public Boolean sprint; public float yaw; public float head_yaw; @@ -21,94 +19,118 @@ public class InputEvent extends RecordEvent { public float pitch; public float speed; - public InputEvent(Boolean sneaking, - Boolean jumping, - Float movementSideways, - Float movementForward, - Boolean pressingForward, - Boolean pressingBack, - Boolean pressingLeft, - Boolean pressingRight, + public static InputEvent current() { + if (Main.client.player == null) return null; + return new InputEvent( + Main.client.player.input.playerInput.forward(), + Main.client.player.input.playerInput.backward(), + Main.client.player.input.playerInput.left(), + Main.client.player.input.playerInput.right(), + Main.client.player.input.playerInput.jump(), + Main.client.player.input.playerInput.sneak(), + Main.client.player.input.playerInput.sprint(), + Main.client.player.getHeadYaw(), + Main.client.player.getBodyYaw(), + Main.client.player.getPitch(), + Main.client.player.getYaw(), + Main.client.player.getMovementSpeed() + ); + } + + public InputEvent(Boolean forward, + Boolean backward, + Boolean left, + Boolean right, + Boolean jump, + Boolean sneak, + Boolean sprint, float head_yaw, float body_yaw, float head_pitch, - Boolean sprinting, float yaw, float speed) { - this.sneaking = sneaking; - this.jumping = jumping; - this.movementSideways = movementSideways; - this.movementForward = movementForward; - this.pressingForward = pressingForward; - this.pressingBack = pressingBack; - this.pressingLeft = pressingLeft; - this.pressingRight = pressingRight; + this.forward = forward; + this.backward = backward; + this.left = left; + this.right = right; + this.jump = jump; + this.sneak = sneak; + this.sprint = sprint; + this.head_yaw = head_yaw; this.body_yaw = body_yaw; this.pitch = head_pitch; - this.sprinting = sprinting; this.yaw = yaw; this.speed = speed; } + /** + * Returns differences of this InputEvent to the provided one, saving first booleans if differ and first floats always + */ + public InputEvent differs(InputEvent event) { + return new InputEvent( + forward == event.forward ? null : forward, + backward == event.backward ? null : backward, + left == event.left ? null : left, + right == event.right ? null : right, + jump == event.jump ? null : jump, + sneak == event.sneak ? null : sneak, + sprint == event.sprint ? null : sprint, + head_yaw, + body_yaw, + pitch, + yaw, + speed + ); + } + public static InputEvent deserialize(String[] a) { return new InputEvent( - (a[0].equals("n") ? null : a[0].equals("1")), - (a[1].equals("n") ? null : a[1].equals("1")), - (a[2].equals("n") ? null : Float.parseFloat(a[2])), - (a[3].equals("n") ? null : Float.parseFloat(a[3])), - (a[4].equals("n") ? null : a[4].equals("1")), - (a[5].equals("n") ? null : a[5].equals("1")), - (a[6].equals("n") ? null : a[6].equals("1")), - (a[7].equals("n") ? null : a[7].equals("1")), - Float.parseFloat(a[8]), Float.parseFloat(a[9]), - Float.parseFloat(a[10]), - (a[11].equals("n") ? null : a[11].equals("1")), - Float.parseFloat(a[12]), - Float.parseFloat(a[13])); + (a[0].equals("n") ? null : a[0].equals("1")), + (a[1].equals("n") ? null : a[1].equals("1")), + (a[2].equals("n") ? null : a[2].equals("1")), + (a[3].equals("n") ? null : a[3].equals("1")), + (a[4].equals("n") ? null : a[4].equals("1")), + (a[5].equals("n") ? null : a[5].equals("1")), + (a[6].equals("n") ? null : a[6].equals("1")), + Float.parseFloat(a[7]), + Float.parseFloat(a[8]), + Float.parseFloat(a[9]), + Float.parseFloat(a[10]), + Float.parseFloat(a[11]) + ); } protected String[] serializeArgs() { return new String[] { - ((sneaking == null) ? "n" : (sneaking ? "1" : "0")), // sneaking - ((jumping == null) ? "n" : (jumping ? "1" : "0")), // jumping - ((movementSideways == null) ? "n" : String.valueOf(movementSideways)), // movement sideways - ((movementForward == null) ? "n" : String.valueOf(movementForward)), // movement forward - ((pressingForward == null) ? "n" : (pressingForward ? "1" : "0")), // pressing forward - ((pressingBack == null) ? "n" : (pressingBack ? "1" : "0")), // pressing back - ((pressingLeft == null) ? "n" : (pressingLeft ? "1" : "0")), // pressing left - ((pressingRight == null) ? "n" : (pressingRight ? "1" : "0")), // pressing right - String.valueOf(head_yaw), // head yaw - String.valueOf(body_yaw), // body yaw - String.valueOf(pitch), // pitch - ((sprinting == null) ? "n" : (sprinting ? "1" : "0")), // sprinting - String.valueOf(yaw), // yaw - String.valueOf(speed) // speed + ((forward == null) ? "n" : (forward ? "1" : "0")), + ((backward == null) ? "n" : (backward ? "1" : "0")), + ((left == null) ? "n" : (left ? "1" : "0")), + ((right == null) ? "n" : (right ? "1" : "0")), + ((jump == null) ? "n" : (jump ? "1" : "0")), + ((sneak == null) ? "n" : (sneak ? "1" : "0")), + ((sprint == null) ? "n" : (sprint ? "1" : "0")), + String.valueOf(head_yaw), + String.valueOf(body_yaw), + String.valueOf(pitch), + String.valueOf(yaw), + String.valueOf(speed), }; } - public void fillEmpty(InputEvent e) { - if (sneaking == null) sneaking = e.sneaking; - if (jumping == null) jumping = e.jumping; - if (movementSideways == null) movementSideways = e.movementSideways; - if (movementForward == null) movementForward = e.movementForward; - if (pressingForward == null) pressingForward = e.pressingForward; - if (pressingBack == null) pressingBack = e.pressingBack; - if (pressingLeft == null) pressingLeft = e.pressingLeft; - if (pressingRight == null) pressingRight = e.pressingRight; - if (sprinting == null) sprinting = e.sprinting; - } - - public boolean isEmpty() { - return sneaking == null && - jumping == null && - movementSideways == null && - movementForward == null && - pressingForward == null && - pressingBack == null && - pressingLeft == null && - pressingRight == null && - sprinting == null; + public boolean equals(InputEvent event) { + return event.forward == forward && + event.backward == backward && + event.sprint == sprint && + event.jump == jump && + event.sneak == sneak && + event.left == left && + event.right == right && + event.speed == speed && + event.head_yaw == head_yaw && + event.body_yaw == body_yaw && + event.yaw == yaw && + event.pitch == pitch; } public void replay() { @@ -117,8 +139,8 @@ public class InputEvent extends RecordEvent { public void inputCallback() { if (Main.client.player != null) { - if (sprinting != null && Main.client.player.isSprinting() != sprinting) - Main.client.player.setSprinting(sprinting); + if (sprint != null && Main.client.player.isSprinting() != sprint) + Main.client.player.setSprinting(sprint); if (Main.client.player.getYaw() != yaw) Main.client.player.setYaw(yaw); if (Main.client.player.getHeadYaw() != head_yaw) @@ -129,22 +151,18 @@ public class InputEvent extends RecordEvent { Main.client.player.setPitch(pitch); if (Main.client.player.getMovementSpeed() != speed) Main.client.player.setMovementSpeed(speed); - if (sneaking != null && Main.client.player.input.sneaking != sneaking) - Main.client.player.input.sneaking = sneaking; - if (jumping != null && Main.client.player.input.jumping != jumping) - Main.client.player.input.jumping = jumping; - if (movementSideways != null && Main.client.player.input.movementSideways != movementSideways) - Main.client.player.input.movementSideways = movementSideways; - if (movementForward != null && Main.client.player.input.movementForward != movementForward) - Main.client.player.input.movementForward = movementForward; - if (pressingForward != null && Main.client.player.input.pressingForward != pressingForward) - Main.client.player.input.pressingForward = pressingForward; - if (pressingBack != null && Main.client.player.input.pressingBack != pressingBack) - Main.client.player.input.pressingBack = pressingBack; - if (pressingLeft != null && Main.client.player.input.pressingLeft != pressingLeft) - Main.client.player.input.pressingLeft = pressingLeft; - if (pressingRight != null && Main.client.player.input.pressingRight != pressingRight) - Main.client.player.input.pressingRight = pressingRight; + + PlayerInput input = Main.client.player.input.playerInput; + + Main.client.player.input.playerInput = new PlayerInput( + this.forward == null ? input.forward() : this.forward, + this.backward == null ? input.backward() : this.backward, + this.left == null ? input.left() : this.left, + this.right == null ? input.right() : this.right, + this.jump == null ? input.jump() : this.jump, + this.sneak == null ? input.sneak() : this.sneak, + this.sprint == null ? input.sprint() : this.sprint + ); } } } diff --git a/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java index 9bf8341..93257cc 100644 --- a/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/EntityMixin.java @@ -20,8 +20,8 @@ public abstract class EntityMixin { if (getUuid().equals(Main.client.player.getUuid())) { if (Main.me.is_replaying) { if (Main.input_replay != null && - Main.input_replay.sprinting != null && - Main.input_replay.sprinting != sprinting) { + Main.input_replay.sprint != null && + Main.input_replay.sprint != sprinting) { ci.cancel(); } } diff --git a/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java index 34c0d25..0879153 100644 --- a/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java +++ b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java @@ -51,7 +51,7 @@ public class ShaderManager { public String loadImport(boolean inline, String name) { return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8); } - }.readSource(readResourceAsString(resource.get().getInputStream()))); + }.readSource(readResourceAsString(resource.get().getInputStream())).getFirst()); } else file_present = false; glCompileShader(i); if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) { From d101b11c36c0232b3a1cac9da4d8f570f3ce423a Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 15 Jun 2025 16:23:04 +0300 Subject: [PATCH 65/72] fix movements 1.21.2 --- gradle.properties | 2 +- .../event/events/InputEvent.java | 54 ++++++++++++------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/gradle.properties b/gradle.properties index bcf329b..1c01598 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,4 +17,4 @@ mod_version = 1.1.2+1.21.2 maven_group = ru.themixray archives_base_name = repeating-mod -# Compatible with: 1.21, 1.21.1 \ No newline at end of file +# Compatible with: 1.21.2, ? \ No newline at end of file diff --git a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java index e0ce865..ac79afe 100644 --- a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java @@ -18,6 +18,8 @@ public class InputEvent extends RecordEvent { public float body_yaw; public float pitch; public float speed; + public float movementForward; + public float movementSideways; public static InputEvent current() { if (Main.client.player == null) return null; @@ -33,7 +35,9 @@ public class InputEvent extends RecordEvent { Main.client.player.getBodyYaw(), Main.client.player.getPitch(), Main.client.player.getYaw(), - Main.client.player.getMovementSpeed() + Main.client.player.getMovementSpeed(), + Main.client.player.input.movementForward, + Main.client.player.input.movementSideways ); } @@ -48,7 +52,9 @@ public class InputEvent extends RecordEvent { float body_yaw, float head_pitch, float yaw, - float speed) { + float speed, + float movementForward, + float movementSideways) { this.forward = forward; this.backward = backward; this.left = left; @@ -62,6 +68,8 @@ public class InputEvent extends RecordEvent { this.pitch = head_pitch; this.yaw = yaw; this.speed = speed; + this.movementForward = movementForward; + this.movementSideways = movementSideways; } /** @@ -80,7 +88,9 @@ public class InputEvent extends RecordEvent { body_yaw, pitch, yaw, - speed + speed, + movementForward, + movementSideways ); } @@ -97,24 +107,28 @@ public class InputEvent extends RecordEvent { Float.parseFloat(a[8]), Float.parseFloat(a[9]), Float.parseFloat(a[10]), - Float.parseFloat(a[11]) + Float.parseFloat(a[11]), + Float.parseFloat(a[12]), + Float.parseFloat(a[13]) ); } protected String[] serializeArgs() { return new String[] { - ((forward == null) ? "n" : (forward ? "1" : "0")), - ((backward == null) ? "n" : (backward ? "1" : "0")), - ((left == null) ? "n" : (left ? "1" : "0")), - ((right == null) ? "n" : (right ? "1" : "0")), - ((jump == null) ? "n" : (jump ? "1" : "0")), - ((sneak == null) ? "n" : (sneak ? "1" : "0")), - ((sprint == null) ? "n" : (sprint ? "1" : "0")), - String.valueOf(head_yaw), - String.valueOf(body_yaw), - String.valueOf(pitch), - String.valueOf(yaw), - String.valueOf(speed), + ((forward == null) ? "n" : (forward ? "1" : "0")), + ((backward == null) ? "n" : (backward ? "1" : "0")), + ((left == null) ? "n" : (left ? "1" : "0")), + ((right == null) ? "n" : (right ? "1" : "0")), + ((jump == null) ? "n" : (jump ? "1" : "0")), + ((sneak == null) ? "n" : (sneak ? "1" : "0")), + ((sprint == null) ? "n" : (sprint ? "1" : "0")), + String.valueOf(head_yaw), + String.valueOf(body_yaw), + String.valueOf(pitch), + String.valueOf(yaw), + String.valueOf(speed), + String.valueOf(movementForward), + String.valueOf(movementSideways) }; } @@ -130,7 +144,9 @@ public class InputEvent extends RecordEvent { event.head_yaw == head_yaw && event.body_yaw == body_yaw && event.yaw == yaw && - event.pitch == pitch; + event.pitch == pitch && + event.movementForward == movementForward && + event.movementSideways == movementSideways; } public void replay() { @@ -152,8 +168,10 @@ public class InputEvent extends RecordEvent { if (Main.client.player.getMovementSpeed() != speed) Main.client.player.setMovementSpeed(speed); - PlayerInput input = Main.client.player.input.playerInput; + Main.client.player.input.movementSideways = movementSideways; + Main.client.player.input.movementForward = movementForward; + PlayerInput input = Main.client.player.input.playerInput; Main.client.player.input.playerInput = new PlayerInput( this.forward == null ? input.forward() : this.forward, this.backward == null ? input.backward() : this.backward, From 06634a8704ab69a6531cc0ce43648a4eed528d2e Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 15 Jun 2025 19:59:29 +0300 Subject: [PATCH 66/72] set license wtfpl in mod --- src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9bbbbe2..4d3612f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "sources": "https://github.com/MeexReay/repeating-mod" }, - "license": "CC0-1.0", + "license": "WTFPL", "icon": "icon.png", "environment": "client", From f0827884065dfafe4e2737ef01bafdc3b7e7684e Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 15 Jun 2025 20:06:31 +0300 Subject: [PATCH 67/72] set fabric mod json 1.21. --- src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 4d3612f..88e2fff 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "depends": { "fabricloader": ">=0.14.14", "fabric-api": "*", - "minecraft": ">=1.20", + "minecraft": ">=1.21.2", "java": ">=17" }, "suggests": { From c65e9065006e9a04da8b176c05a6e79ace667b90 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sun, 15 Jun 2025 20:36:55 +0300 Subject: [PATCH 68/72] port to 1.21.4, RECORDS LIST IS BROKEN --- gradle.properties | 10 +++++----- .../ru/themixray/repeating_mod/mixin/InputMixin.java | 2 +- .../repeating_mod/widget/RecordListWidget.java | 12 ++++++------ src/main/resources/fabric.mod.json | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/gradle.properties b/gradle.properties index 1c01598..63462f5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,17 +4,17 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.2 -yarn_mappings=1.21.2+build.1 +minecraft_version=1.21.4 +yarn_mappings=1.21.4+build.8 loader_version=0.16.14 loom_version=1.10-SNAPSHOT # Fabric API -fabric_version=0.106.1+1.21.2 +fabric_version=0.119.3+1.21.4 # Mod Properties -mod_version = 1.1.2+1.21.2 +mod_version = 1.1.2+1.21.4 maven_group = ru.themixray archives_base_name = repeating-mod -# Compatible with: 1.21.2, ? \ No newline at end of file +# Compatible with: 1.21.4, ? \ No newline at end of file diff --git a/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java b/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java index 33acbf1..261c59c 100644 --- a/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java +++ b/src/main/java/ru/themixray/repeating_mod/mixin/InputMixin.java @@ -10,7 +10,7 @@ import ru.themixray.repeating_mod.Main; @Mixin(KeyboardInput.class) public abstract class InputMixin { @Inject(at = @At(value = "TAIL"), method = "tick") - private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { + private void onTickTail(CallbackInfo ci) { if (Main.me.is_replaying) { if (Main.input_replay != null) { Main.input_replay.inputCallback(); diff --git a/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java index 5b7a7e3..6ab167d 100644 --- a/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java +++ b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java @@ -26,13 +26,18 @@ public class RecordListWidget extends ScrollableWidget { return res; } + @Override + protected int getContentsHeightWithPadding() { + return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0; + } + @Override protected double getDeltaYPerScroll() { return 10; } @Override - protected void renderContents(DrawContext ctx, int mouseX, int mouseY, float delta) { + protected void renderWidget(DrawContext ctx, int mouseX, int mouseY, float delta) { int y = 0; for (RecordWidget wid: widgets) { wid.setY(y); @@ -63,11 +68,6 @@ public class RecordListWidget extends ScrollableWidget { return focused; } - @Override - protected int getContentsHeight() { - return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0; - } - public void init(RepeatingScreen screen) { for (RecordWidget widget : widgets) { widget.init(screen); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 88e2fff..4a40393 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "depends": { "fabricloader": ">=0.14.14", "fabric-api": "*", - "minecraft": ">=1.21.2", + "minecraft": ">=1.21.4", "java": ">=17" }, "suggests": { From ac1ee8ec6da34e9cbc11d8f05b3875b339c4724c Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 18 Jun 2025 15:57:29 +0300 Subject: [PATCH 69/72] fix record list widget --- .../repeating_mod/widget/RecordListWidget.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java index 6ab167d..b17af92 100644 --- a/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java +++ b/src/main/java/ru/themixray/repeating_mod/widget/RecordListWidget.java @@ -38,20 +38,28 @@ public class RecordListWidget extends ScrollableWidget { @Override protected void renderWidget(DrawContext ctx, int mouseX, int mouseY, float delta) { - int y = 0; + ctx.fill(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight(), 0xff000000); + + ctx.enableScissor(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight()); + + int y = (int) -this.getScrollY(); for (RecordWidget wid: widgets) { wid.setY(y); - wid.render(ctx, mouseX, (int) (mouseY + this.getScrollY()), delta); + wid.render(ctx, mouseX, (int) (mouseY), delta); y += wid.getHeight(); y += 2; } + + ctx.disableScissor(); + + this.drawScrollbar(ctx); } public void addWidget(RecordState record) { - RecordWidget widget = new RecordWidget(0, 0, width, 55, record, this); + RecordWidget widget = new RecordWidget(0, 0, width - 6, 55, record, this); widget.init(null); - widgets.add(0, widget); + widgets.addFirst(widget); } public void removeWidget(RecordState record) { @@ -125,7 +133,7 @@ public class RecordListWidget extends ScrollableWidget { @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { - return checkTransportNF(mouseX, mouseY + getScrollY(), button) || super.mouseClicked(mouseX, mouseY, button); + return checkTransportNF(mouseX, mouseY, button) || super.checkScrollbarDragged(mouseX, mouseY, button); } @Override From 59a2175fb2e70b25ac67bb53e8a5c7312abe104f Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 18 Jun 2025 16:18:48 +0300 Subject: [PATCH 70/72] port to 1.21.5 --- gradle.properties | 10 +++++----- .../event/events/BlockInteractEvent.java | 4 ++-- .../repeating_mod/event/events/InputEvent.java | 17 +++++++++++++---- .../render/shader/ShaderManager.java | 2 +- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/gradle.properties b/gradle.properties index 63462f5..68634eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,17 +4,17 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 +minecraft_version=1.21.5 +yarn_mappings=1.21.5+build.1 loader_version=0.16.14 loom_version=1.10-SNAPSHOT # Fabric API -fabric_version=0.119.3+1.21.4 +fabric_version=0.127.0+1.21.5 # Mod Properties -mod_version = 1.1.2+1.21.4 +mod_version = 1.1.2+1.21.5 maven_group = ru.themixray archives_base_name = repeating-mod -# Compatible with: 1.21.4, ? \ No newline at end of file +# Compatible with: 1.21.5 \ No newline at end of file diff --git a/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java index 789ea8f..16a8425 100644 --- a/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/BlockInteractEvent.java @@ -19,7 +19,7 @@ public class BlockInteractEvent extends RecordEvent { Double.parseDouble(a[0]), Double.parseDouble(a[1]), Double.parseDouble(a[2])), - Direction.byId(Integer.parseInt(a[4])), + Direction.byIndex(Integer.parseInt(a[4])), new BlockPos( Integer.parseInt(a[0]), Integer.parseInt(a[1]), @@ -44,7 +44,7 @@ public class BlockInteractEvent extends RecordEvent { String.valueOf(hitResult.getBlockPos().getY()), String.valueOf(hitResult.getBlockPos().getZ()), (hitResult.isInsideBlock() ? "1" : "0"), - String.valueOf(hitResult.getSide().getId()), + String.valueOf(hitResult.getSide().getIndex()), hand.name() }; } diff --git a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java index ac79afe..01d1f39 100644 --- a/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java +++ b/src/main/java/ru/themixray/repeating_mod/event/events/InputEvent.java @@ -1,9 +1,13 @@ package ru.themixray.repeating_mod.event.events; +import net.minecraft.client.input.Input; import net.minecraft.util.PlayerInput; +import net.minecraft.util.math.Vec2f; import ru.themixray.repeating_mod.Main; import ru.themixray.repeating_mod.event.RecordEvent; +import java.lang.reflect.Field; + public class InputEvent extends RecordEvent { public Boolean forward; public Boolean backward; @@ -36,8 +40,8 @@ public class InputEvent extends RecordEvent { Main.client.player.getPitch(), Main.client.player.getYaw(), Main.client.player.getMovementSpeed(), - Main.client.player.input.movementForward, - Main.client.player.input.movementSideways + Main.client.player.input.getMovementInput().y, + Main.client.player.input.getMovementInput().x ); } @@ -168,8 +172,13 @@ public class InputEvent extends RecordEvent { if (Main.client.player.getMovementSpeed() != speed) Main.client.player.setMovementSpeed(speed); - Main.client.player.input.movementSideways = movementSideways; - Main.client.player.input.movementForward = movementForward; + try { + Field field = Input.class.getDeclaredField("movementVector"); + field.setAccessible(true); + field.set(Main.client.player.input, new Vec2f(movementSideways, movementForward)); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } PlayerInput input = Main.client.player.input.playerInput; Main.client.player.input.playerInput = new PlayerInput( diff --git a/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java index 0879153..f5e26a6 100644 --- a/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java +++ b/src/main/java/ru/themixray/repeating_mod/render/shader/ShaderManager.java @@ -1,6 +1,6 @@ package ru.themixray.repeating_mod.render.shader; -import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.platform.TextureUtil; import lombok.Getter; import lombok.SneakyThrows; From 0b3bac264f036b5b4166b72f6c8b8c0cb11053bb Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 18 Jun 2025 18:17:57 +0300 Subject: [PATCH 71/72] port to 1.21.6 --- gradle.properties | 6 +++--- .../repeating_mod/RepeatingScreen.java | 2 +- .../repeating_mod/widget/RecordWidget.java | 17 +++++------------ 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/gradle.properties b/gradle.properties index 68634eb..a363b27 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,13 +4,13 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.5 -yarn_mappings=1.21.5+build.1 +minecraft_version=1.21.6 +yarn_mappings=1.21.6+build.1 loader_version=0.16.14 loom_version=1.10-SNAPSHOT # Fabric API -fabric_version=0.127.0+1.21.5 +fabric_version=0.127.0+1.21.6 # Mod Properties mod_version = 1.1.2+1.21.5 diff --git a/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java index 8d95918..0783386 100644 --- a/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java +++ b/src/main/java/ru/themixray/repeating_mod/RepeatingScreen.java @@ -51,7 +51,7 @@ public class RepeatingScreen extends Screen { @Override public void render(DrawContext context, int mouseX, int mouseY, float delta) { - renderBackground(context, mouseX, mouseY, delta); +// renderBackground(context, mouseX, mouseY, delta); for (RenderListener l : render_listeners) { if (l.beforeRender()) { diff --git a/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java b/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java index 7d53358..5b9954e 100644 --- a/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java +++ b/src/main/java/ru/themixray/repeating_mod/widget/RecordWidget.java @@ -1,5 +1,6 @@ package ru.themixray.repeating_mod.widget; +import lombok.Getter; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.Drawable; import net.minecraft.client.gui.tooltip.Tooltip; @@ -18,6 +19,7 @@ import java.util.List; import java.util.function.Consumer; public class RecordWidget implements Drawable, Widget { + @Getter private RecordState record; private List children; @@ -150,22 +152,13 @@ public class RecordWidget implements Drawable, Widget { children.add(replay_button); } - public RecordState getRecord() { - return record; - } - - public void drawText(int x, int y, DrawContext ctx, List lines, float size, int line_height, boolean shadow) { - ctx.getMatrices().push(); - ctx.getMatrices().scale(size, size, size); - + public void drawText(int x, int y, DrawContext ctx, List lines, int line_height, boolean shadow) { int now_y = y; for (Text line : lines) { - ctx.drawText(Main.client.textRenderer, line, (int) (x / size), (int) (now_y / size), line.getStyle().getColor().getRgb(), shadow); + ctx.drawText(Main.client.textRenderer, line, x, now_y, 0xff000000 + line.getStyle().getColor().getRgb(), shadow); now_y += line_height; } - - ctx.getMatrices().pop(); } @Override @@ -190,7 +183,7 @@ public class RecordWidget implements Drawable, Widget { .append(": ") .styled((s) -> s.withColor(0xbbbbbb)), Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) - ), 1, + ), 9, false); From 654589355d73c910bbe987f9e22f986cb924b367 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Wed, 18 Jun 2025 18:18:55 +0300 Subject: [PATCH 72/72] fix version name --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index a363b27..780fbef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,8 @@ loom_version=1.10-SNAPSHOT fabric_version=0.127.0+1.21.6 # Mod Properties -mod_version = 1.1.2+1.21.5 +mod_version = 1.1.2+1.21.6 maven_group = ru.themixray archives_base_name = repeating-mod -# Compatible with: 1.21.5 \ No newline at end of file +# Compatible with: 1.21.6 \ No newline at end of file