fix records list and add btns

This commit is contained in:
MeexReay 2024-04-22 00:25:43 +03:00
parent b6589b3629
commit 38dad10048
24 changed files with 758 additions and 286 deletions

View File

@ -4,12 +4,12 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.20 minecraft_version=1.20.1
yarn_mappings=1.20+build.1 yarn_mappings=1.20.1+build.10
loader_version=0.14.23 loader_version=0.15.10
#Fabric api #Fabric api
fabric_version=0.83.0+1.20 fabric_version=0.92.1+1.20.1
# Mod Properties # Mod Properties
mod_version = 1.0.6 mod_version = 1.0.6

View File

@ -1,6 +1,5 @@
package themixray.repeating.mod; package themixray.repeating.mod;
import com.google.common.collect.Lists;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 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.keybinding.v1.KeyBindingHelper;
@ -16,28 +15,29 @@ import net.minecraft.util.math.Vec3d;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import themixray.repeating.mod.events.RecordDelayEvent; import themixray.repeating.mod.event.RecordDelayEvent;
import themixray.repeating.mod.events.RecordEvent; import themixray.repeating.mod.event.RecordEvent;
import themixray.repeating.mod.events.RecordInputEvent; import themixray.repeating.mod.event.RecordInputEvent;
import themixray.repeating.mod.events.RecordMoveEvent; import themixray.repeating.mod.event.RecordMoveEvent;
import themixray.repeating.mod.render.RenderHelper; import themixray.repeating.mod.render.RenderHelper;
import themixray.repeating.mod.render.RenderSystem; import themixray.repeating.mod.render.RenderSystem;
import themixray.repeating.mod.render.buffer.WorldBuffer; import themixray.repeating.mod.render.buffer.WorldBuffer;
import java.awt.*; import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.List; 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 Logger LOGGER = LoggerFactory.getLogger("repeating-mod");
public static final MinecraftClient client = MinecraftClient.getInstance(); public static final MinecraftClient client = MinecraftClient.getInstance();
public static final FabricLoader loader = FabricLoader.getInstance(); public static final FabricLoader loader = FabricLoader.getInstance();
public static RepeatingMod me; public static Main me;
public Vec3d start_record_pos = null; public RecordList record_list;
public Vec3d finish_record_pos = null; public RecordState now_record;
public List<RecordEvent> record = new ArrayList<>();
public boolean is_recording = false; public boolean is_recording = false;
public long last_record = -1; public long last_record = -1;
public TickTask move_tick = null; public TickTask move_tick = null;
@ -59,52 +59,30 @@ public class RepeatingMod implements ClientModInitializer {
public static Random rand = new Random(); public static Random rand = new Random();
public EasyConfig conf; public EasyConfig conf;
public File records_folder;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
LOGGER.info("Repeating mod initialized"); LOGGER.info("Repeating mod initialized");
me = this; 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(); RenderSystem.init();
WorldRenderEvents.LAST.register(context -> { WorldRenderEvents.LAST.register(context -> {
WorldBuffer buffer = RenderHelper.startTri(context); WorldBuffer buffer = RenderHelper.startTri(context);
if (start_record_pos != null) { if (now_record != null) {
RenderHelper.drawRectFromTri(buffer, Vec3d start_pos = now_record.getStartRecordPos();
(float) start_record_pos.getX() - 0.25F, Vec3d finish_pos = now_record.getFinishRecordPos();
(float) start_record_pos.getY() + 0.01F,
(float) start_record_pos.getZ() - 0.25F,
(float) start_record_pos.getX() + 0.25F, if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 70, 128));
(float) start_record_pos.getY() + 0.01F, if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128));
(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); RenderHelper.endTri(buffer);
}); });
@ -131,11 +109,13 @@ public class RepeatingMod implements ClientModInitializer {
if (menu_key.wasPressed()) if (menu_key.wasPressed())
client.setScreen(menu); client.setScreen(menu);
if (toggle_replay_key.wasPressed()) { if (toggle_replay_key.wasPressed()) {
if (!is_recording) { if (now_record != null) {
if (is_replaying) if (!is_recording) {
stopReplay(); if (is_replaying)
else startReplay(); stopReplay();
menu.update_btns(); else startReplay();
menu.updateButtons();
}
} }
} }
if (toggle_record_key.wasPressed()) { if (toggle_record_key.wasPressed()) {
@ -143,7 +123,7 @@ public class RepeatingMod implements ClientModInitializer {
if (is_recording) if (is_recording)
stopRecording(); stopRecording();
else startRecording(); else startRecording();
menu.update_btns(); menu.updateButtons();
} }
} }
}); });
@ -154,26 +134,43 @@ public class RepeatingMod implements ClientModInitializer {
living_ticks++; living_ticks++;
} }
}; };
System.setProperty("java.awt.headless", "false");
} }
public RecordEvent getLastRecord(String t) { public void setNowRecord(RecordState record) {
for (RecordEvent r:Lists.reverse(new ArrayList<>(record))) { now_record = record;
if (r.getType().equals(t)) { }
return r;
} public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) {
} RenderHelper.drawRectFromTri(buffer,
return null; (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() { public void startRecording() {
is_recording = true; is_recording = true;
menu.update_btns(); menu.updateButtons();
record.clear();
finish_record_pos = null; now_record = record_list.newRecord();
Vec3d v = client.player.getPos();
record.add(new RecordMoveEvent(v,client.player.getHeadYaw(),client.player.getPitch())); Vec3d start_pos = client.player.getPos();
start_record_pos = v; now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch()));
now_record.setStartRecordPos(start_pos);
if (record_pos_delay > 0) { if (record_pos_delay > 0) {
move_tick = new TickTask( move_tick = new TickTask(
@ -181,7 +178,7 @@ public class RepeatingMod implements ClientModInitializer {
record_pos_delay) { record_pos_delay) {
@Override @Override
public void run() { public void run() {
record.add(new RecordMoveEvent(client.player.getPos(), now_record.addEvent(new RecordMoveEvent(client.player.getPos(),
client.player.getHeadYaw(), client.player.getPitch())); client.player.getHeadYaw(), client.player.getPitch()));
} }
}; };
@ -195,15 +192,20 @@ public class RepeatingMod implements ClientModInitializer {
long now = living_ticks; long now = living_ticks;
if (last_record != -1) { if (last_record != -1) {
long diff = now - last_record - 2; 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; last_record = now;
} }
} }
public void recordAllInput() { public void recordAllInput() {
RecordInputEvent l = ((RecordInputEvent)getLastRecord("input")); if (client.player == null) {
stopRecording();
return;
}
RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input"));
if (l == null) { if (l == null) {
RecordInputEvent e = new RecordInputEvent( RecordInputEvent e = new RecordInputEvent(
client.player.input.sneaking, 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.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack,
((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, ((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft,
((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, ((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(), ((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(),
client.player.getYaw(),client.player.getMovementSpeed()); client.player.getYaw(),client.player.getMovementSpeed());
@ -248,12 +250,17 @@ public class RepeatingMod implements ClientModInitializer {
public void stopRecording() { public void stopRecording() {
is_recording = false; is_recording = false;
finish_record_pos = client.player.getPos(); now_record.setFinishRecordPos(client.player.getPos());
if (move_tick != null) { try {
now_record.save();
} catch (IOException e) {
throw new RuntimeException(e);
}
if (move_tick != null) {
move_tick.cancel(); move_tick.cancel();
move_tick = null; move_tick = null;
} }
menu.update_btns(); menu.updateButtons();
last_record = -1; last_record = -1;
sendMessage(Text.translatable("message.repeating-mod.record_stop")); sendMessage(Text.translatable("message.repeating-mod.record_stop"));
} }
@ -262,14 +269,17 @@ public class RepeatingMod implements ClientModInitializer {
public void startReplay() { public void startReplay() {
is_recording = false; is_recording = false;
is_replaying = true; is_replaying = true;
menu.update_btns(); menu.updateButtons();
List<RecordEvent> events = now_record.getEvents();
replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) {
public int replay_index = 0; public int replay_index = 0;
@Override @Override
public void run() { public void run() {
if (!is_replaying) cancel(); if (!is_replaying) cancel();
RecordEvent e = record.get(replay_index); RecordEvent e = events.get(replay_index);
if (e instanceof RecordDelayEvent) { if (e instanceof RecordDelayEvent) {
setDelay(((RecordDelayEvent) e).delay); setDelay(((RecordDelayEvent) e).delay);
} else { } else {
@ -278,11 +288,11 @@ public class RepeatingMod implements ClientModInitializer {
replay_index++; replay_index++;
if (!loop_replay) { if (!loop_replay) {
if (replay_index == record.size()) { if (replay_index == events.size()) {
stopReplay(); stopReplay();
cancel(); cancel();
} }
} else if (replay_index == record.size()) { } else if (replay_index == events.size()) {
replay_index = 0; replay_index = 0;
} }
} }
@ -298,7 +308,8 @@ public class RepeatingMod implements ClientModInitializer {
replay_tick.cancel(); replay_tick.cancel();
replay_tick = null; 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")); sendMessage(Text.translatable("message.repeating-mod.replay_stop"));
} }

View File

@ -1,5 +1,77 @@
package themixray.repeating.mod; 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<RecordState> 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<RecordState> 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;
}
} }

View File

@ -1,32 +1,38 @@
package themixray.repeating.mod; package themixray.repeating.mod;
import net.minecraft.text.Text; import com.google.common.collect.Lists;
import net.minecraft.util.math.Vec3d; 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.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; 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 final File file;
private String name; private String name;
private String date; private Date date;
private String author; private String author;
private List<RecordEvent> events; private List<RecordEvent> events;
private Vec3d start_record_pos; private Vec3d start_record_pos;
private Vec3d finish_record_pos; private Vec3d finish_record_pos;
public RecordFile(File file, public RecordState(File file,
String name, String name,
String date, Date date,
String author, String author,
List<RecordEvent> events, List<RecordEvent> events,
Vec3d start_record_pos, Vec3d start_record_pos,
Vec3d finish_record_pos) { Vec3d finish_record_pos) {
this.file = file; this.file = file;
this.name = name; this.name = name;
this.date = date; this.date = date;
@ -49,15 +55,27 @@ public class RecordFile {
return author; return author;
} }
public String getDate() { public Date getDate() {
return date; return date;
} }
public List<RecordEvent> getEvents() {
return events;
}
public Vec3d getFinishRecordPos() {
return finish_record_pos;
}
public Vec3d getStartRecordPos() {
return start_record_pos;
}
public void setAuthor(String author) { public void setAuthor(String author) {
this.author = author; this.author = author;
} }
public void setDate(String date) { public void setDate(Date date) {
this.date = date; this.date = date;
} }
@ -65,26 +83,66 @@ public class RecordFile {
this.name = name; this.name = name;
} }
public void writeToMod() { public void setEvents(List<RecordEvent> 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()); String text = Files.readString(file.toPath());
List<String> lines = List.of(text.split("\n")); List<String> lines = List.of(text.split("\n"));
List<String> signature = lines.subList(0,4); List<String> signature = lines.subList(0,4);
String name = signature.get(0); String name = signature.get(0);
String data = signature.get(1); Date date = DATE_FORMAT.parse(signature.get(1));
String author = signature.get(2); String author = signature.get(2);
String record_pos = signature.get(3); String record_pos = signature.get(3);
@ -105,6 +163,12 @@ public class RecordFile {
List<String> event_lines = lines.subList(4,lines.size()); List<String> event_lines = lines.subList(4,lines.size());
List<RecordEvent> events = event_lines.stream().map(RecordEvent::deserialize).toList(); List<RecordEvent> 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);
} }
} }

View File

@ -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);
}

View File

@ -2,182 +2,99 @@ package themixray.repeating.mod;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.gui.DrawContext; 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.screen.Screen;
import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.SliderWidget; import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.text.Text; 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.awt.*;
import java.io.File; import java.io.File;
import java.nio.file.Files; import java.util.ArrayList;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List; import java.util.List;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class RepeatingScreen extends Screen { public class RepeatingScreen extends Screen {
protected RepeatingScreen() { private static List<RenderListener> render_listeners = new ArrayList<>();
super(Text.literal(""));
this.mod = RepeatingMod.me;
}
public RepeatingMod mod;
public ButtonWidget record_btn; public ButtonWidget record_btn;
public ButtonWidget replay_btn;
public ButtonWidget loop_btn; public ButtonWidget loop_btn;
public ButtonWidget export_btn;
public ButtonWidget import_btn; public ButtonWidget import_btn;
public SliderWidget pos_delay_slider; public SliderWidget pos_delay_slider;
public boolean was_build = false; 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) { if (was_build) {
replay_btn.setMessage(Text.translatable("text.repeating-mod." + record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record")));
((mod.is_replaying) ? "stop_replay" : "start_replay"))); loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop")));
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 ")));
} }
} }
@Override @Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) { public void render(DrawContext context, int mouseX, int mouseY, float delta) {
renderBackground(context); renderBackground(context);
for (RenderListener l : render_listeners) {
if (l.beforeRender()) {
l.render(context, mouseX, mouseY, delta);
}
}
super.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 @Override
protected void init() { protected void init() {
record_btn = ButtonWidget.builder( record_btn = ButtonWidget.builder(
Text.translatable("text.repeating-mod.start_record"), button -> { Text.translatable("text.repeating-mod.start_record"), button -> {
if (!mod.is_replaying) { if (!Main.me.is_replaying) {
if (mod.is_recording) if (Main.me.is_recording)
mod.stopRecording(); Main.me.stopRecording();
else mod.startRecording(); else Main.me.startRecording();
update_btns(); 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"))) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip")))
.build(); .build();
replay_btn = ButtonWidget.builder( loop_btn = ButtonWidget.builder(Text.empty(), button -> {
Text.translatable("text.repeating-mod.start_replay"), button -> { Main.me.loop_replay = !Main.me.loop_replay;
if (!mod.is_recording) { updateButtons();
if (mod.is_replaying)
mod.stopReplay();
else mod.startReplay();
update_btns();
}
}) })
.dimensions(width / 2 - 60, height / 2 - 32, 98, 20) .dimensions(width / 2 - 60, height / 2 - 54, 120, 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"))) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip")))
.build(); .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<String> 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( pos_delay_slider = new SliderWidget(
width / 2 - 60, height / 2 + 34, 120, 20, width / 2 - 60, height / 2 - 32, 120, 20,
(mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") : (Main.me.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)), Text.translatable("text.repeating-mod.pos_delay", String.valueOf(Main.me.record_pos_delay)),
(mod.record_pos_delay+1d)/101d) { (Main.me.record_pos_delay+1d)/101d) {
@Override @Override
protected void updateMessage() { protected void updateMessage() {
@ -191,9 +108,9 @@ public class RepeatingScreen extends Screen {
double v = value*101d-1d; double v = value*101d-1d;
if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay")); 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))); else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) v)));
mod.record_pos_delay = (long) v; Main.me.record_pos_delay = (long) v;
mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay)); Main.me.conf.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay));
mod.conf.save(); Main.me.conf.save();
} }
@Override @Override
@ -216,15 +133,57 @@ public class RepeatingScreen extends Screen {
}; };
pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); 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; was_build = true;
update_btns(); updateButtons();
addDrawableChild(replay_btn);
addDrawableChild(loop_btn); addDrawableChild(loop_btn);
addDrawableChild(record_btn); addDrawableChild(record_btn);
addDrawableChild(export_btn);
addDrawableChild(import_btn); addDrawableChild(import_btn);
addDrawableChild(pos_delay_slider); addDrawableChild(pos_delay_slider);
} }
public <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement) {
return super.addDrawableChild(drawableElement);
}
public <T extends Drawable> T addDrawable(T drawable) {
return super.addDrawable(drawable);
}
public <T extends Element & Selectable> T addSelectableChild(T child) {
return super.addSelectableChild(child);
}
public void remove(Element child) {
super.remove(child);
}
} }

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import themixray.repeating.mod.Main; import themixray.repeating.mod.Main;

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
public class RecordDelayEvent extends RecordEvent { public class RecordDelayEvent extends RecordEvent {
public long delay; public long delay;

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
public abstract class RecordEvent { public abstract class RecordEvent {
public abstract void replay(); public abstract void replay();

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
import themixray.repeating.mod.Main; import themixray.repeating.mod.Main;

View File

@ -1,4 +1,4 @@
package themixray.repeating.mod.events; package themixray.repeating.mod.event;
import net.minecraft.entity.MovementType; import net.minecraft.entity.MovementType;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;

View File

@ -5,15 +5,15 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.Main;
import themixray.repeating.mod.TickTask; import themixray.repeating.mod.TickTask;
@Mixin(MinecraftClient.class) @Mixin(MinecraftClient.class)
public abstract class ClientMixin { public abstract class ClientMixin {
@Inject(at = @At(value = "HEAD"), method = "tick") @Inject(at = @At(value = "HEAD"), method = "tick")
private void onTickHead(CallbackInfo ci) { private void onTickHead(CallbackInfo ci) {
if (RepeatingMod.me.is_recording) if (Main.me.is_recording)
RepeatingMod.me.recordAllInput(); Main.me.recordAllInput();
TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD);
} }

View File

@ -6,8 +6,7 @@ import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.Main;
import themixray.repeating.mod.TickTask;
import java.util.UUID; import java.util.UUID;
@ -17,11 +16,11 @@ public abstract class EntityMixin {
@Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true) @Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true)
private void onSprint(boolean sprinting,CallbackInfo ci) { private void onSprint(boolean sprinting,CallbackInfo ci) {
if (getUuid().equals(RepeatingMod.client.player.getUuid())) { if (getUuid().equals(Main.client.player.getUuid())) {
if (RepeatingMod.me.is_replaying) { if (Main.me.is_replaying) {
if (RepeatingMod.input_replay != null && if (Main.input_replay != null &&
RepeatingMod.input_replay.sprinting != null && Main.input_replay.sprinting != null &&
RepeatingMod.input_replay.sprinting != sprinting) { Main.input_replay.sprinting != sprinting) {
ci.cancel(); ci.cancel();
return; return;
} }

View File

@ -5,15 +5,15 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.Main;
@Mixin(KeyboardInput.class) @Mixin(KeyboardInput.class)
public abstract class InputMixin { public abstract class InputMixin {
@Inject(at = @At(value = "TAIL"), method = "tick") @Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(boolean slowDown, float f, CallbackInfo ci) { private void onTickTail(boolean slowDown, float f, CallbackInfo ci) {
if (RepeatingMod.me.is_replaying) { if (Main.me.is_replaying) {
if (RepeatingMod.input_replay != null) { if (Main.input_replay != null) {
RepeatingMod.input_replay.inputCallback(); Main.input_replay.inputCallback();
} }
} }
} }

View File

@ -9,9 +9,9 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.events.RecordBlockBreakEvent; import themixray.repeating.mod.Main;
import themixray.repeating.mod.events.RecordBlockInteractEvent; import themixray.repeating.mod.event.RecordBlockBreakEvent;
import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.event.RecordBlockInteractEvent;
import themixray.repeating.mod.TickTask; import themixray.repeating.mod.TickTask;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
@ -20,14 +20,14 @@ public abstract class MovementMixin {
@Inject(at = @At(value = "HEAD"), method = "init") @Inject(at = @At(value = "HEAD"), method = "init")
private void init(CallbackInfo ci) { private void init(CallbackInfo ci) {
PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> { PlayerBlockBreakEvents.AFTER.register((world, player, pos, blockState, blockEntity) -> {
if (RepeatingMod.me.is_recording) if (Main.me.is_recording)
RepeatingMod.me.recordTick(new RecordBlockBreakEvent(pos)); Main.me.recordTick(new RecordBlockBreakEvent(pos));
}); });
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> {
if (hitResult.getType().equals(HitResult.Type.BLOCK)) if (hitResult.getType().equals(HitResult.Type.BLOCK))
if (RepeatingMod.me.is_recording) if (Main.me.is_recording)
RepeatingMod.me.recordTick(new RecordBlockInteractEvent(hand,hitResult)); Main.me.recordTick(new RecordBlockInteractEvent(hand,hitResult));
return ActionResult.PASS; return ActionResult.PASS;
}); });
} }

View File

@ -1,6 +1,5 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.listener.ServerPlayPacketListener; import net.minecraft.network.listener.ServerPlayPacketListener;
import net.minecraft.network.packet.Packet; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask;
import java.time.Duration; import java.time.Duration;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;

View File

@ -1,12 +1,10 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.minecraft.client.input.KeyboardInput;
import net.minecraft.client.render.*; import net.minecraft.client.render.*;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask; import themixray.repeating.mod.TickTask;
@Mixin(GameRenderer.class) @Mixin(GameRenderer.class)

View File

@ -21,28 +21,28 @@ public class BufferManager {
}); });
} }
public void bindBuffer() { public static void bindBuffer() {
glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo);
} }
public void unbindBuffer() { public static void unbindBuffer() {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
public void writeBuffer(FloatBuffer buffer) { public static void writeBuffer(FloatBuffer buffer) {
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW); 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); glDrawArrays(drawMode, 0, verts);
} }
public void bind() { public static void bind() {
prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING); prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING);
glBindVertexArray(vao); glBindVertexArray(vao);
} }
public void unbind() { public static void unbind() {
glBindVertexArray(prevVao); glBindVertexArray(prevVao);
} }
} }

View File

@ -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<RecordWidget> 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);
}
}

View File

@ -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<ClickableWidget> 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<ClickableWidget> getChildren() {
return children;
}
@Override
public void forEachChild(Consumer<ClickableWidget> 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<Text> 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);
}
}
}

View File

@ -11,14 +11,23 @@
"text.repeating-mod.record_tooltip": "Start/stop recording all activities", "text.repeating-mod.record_tooltip": "Start/stop recording all activities",
"text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions", "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.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.import": "Import record",
"text.repeating-mod.export_tooltip": "Exporting a recording to a file", "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.dev": "In development...",
"text.repeating-mod.nan_pos_delay": "No pos timer", "text.repeating-mod.nan_pos_delay": "No pos timer",
"text.repeating-mod.pos_delay": "Pos timer: %s ticks", "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.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_start": "Replay started",
"message.repeating-mod.replay_stop": "Replay finished", "message.repeating-mod.replay_stop": "Replay finished",

View File

@ -10,19 +10,29 @@
"text.repeating-mod.stop_replay": "Остановить повтор", "text.repeating-mod.stop_replay": "Остановить повтор",
"text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий", "text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий",
"text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий", "text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий",
"text.repeating-mod.loop_tooltip": "Вкл/выкл повтор повтора записанных действий", "text.repeating-mod.loop_tooltip": "Вкл/выкл повтор записи",
"text.repeating-mod.export": "Экспорт записи",
"text.repeating-mod.import": "Импорт записи", "text.repeating-mod.import": "Импорт записи",
"text.repeating-mod.export_tooltip": "Экспорт записи в файл", "text.repeating-mod.export_tooltip": "Экспорт записи в файл",
"text.repeating-mod.import_tooltip": "Импорт записи из файла import.txt", "text.repeating-mod.import_tooltip": "Импорт записи из файла",
"text.repeating-mod.dev": "В разработке...", "text.repeating-mod.dev": "В разработке...",
"text.repeating-mod.nan_pos_delay": "Таймера позиции нету", "text.repeating-mod.nan_pos_delay": "Таймера позиции нету",
"text.repeating-mod.pos_delay": "Таймер позиции: %s тиков", "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков",
"text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)", "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_start": "Повтор начат",
"message.repeating-mod.replay_stop": "Повтор закончен", "message.repeating-mod.replay_stop": "Повтор закончен",
"message.repeating-mod.record_start": "Запись начата", "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": "Стоп"
} }

View File

@ -19,7 +19,7 @@
"environment": "client", "environment": "client",
"entrypoints": { "entrypoints": {
"client": [ "client": [
"themixray.repeating.mod.RepeatingMod" "themixray.repeating.mod.Main"
] ]
}, },
"mixins": [ "mixins": [