This commit is contained in:
MeexReay 2024-12-08 02:39:49 +03:00
parent 5d9b27cd9a
commit cc12eab006
50 changed files with 1166 additions and 3483 deletions

View File

@ -1,17 +1,17 @@
# Done to increase the memory available to gradle. # Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true 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.3
loader_version=0.15.10 loader_version=0.15.10
#Fabric api #Fabric api
fabric_version=0.83.0+1.20 fabric_version=0.97.0+1.20.4
# Mod Properties # Mod Properties
mod_version = 1.1.1+1.20 mod_version = 1.1.2+1.20.1
maven_group = themixray.repeating.mod maven_group = themixray.repeating.mod
archives_base_name = repeating-mod archives_base_name = repeating-mod

184
gradlew.bat vendored
View File

@ -1,92 +1,92 @@
@rem @rem
@rem Copyright 2015 the original author or authors. @rem Copyright 2015 the original author or authors.
@rem @rem
@rem Licensed under the Apache License, Version 2.0 (the "License"); @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 not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem https://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @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 distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused @rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter. @rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 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. @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" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2 echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 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. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 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 echo location of your Java installation. 1>&2
goto fail goto fail
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. 1>&2 echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2 echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 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 echo location of your Java installation. 1>&2
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE% exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
:omega :omega

View File

@ -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<String,String> data;
public EasyConfig(File f, Map<String,String> 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<String,String> m:def.entrySet())
if (!data.containsKey(m.getKey()))
data.put(m.getKey(),m.getValue());
save();
}
public EasyConfig(Path f, Map<String,String> def) {
this(f.toFile(),def);
}
public EasyConfig(String parent,String child,Map<String,String> def) {
this(new File(parent,child),def);
}
public EasyConfig(File parent,String child,Map<String,String> def) {
this(new File(parent,child),def);
}
public EasyConfig(Path parent,String child,Map<String,String> 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<String,String> p) {
StringBuilder t = new StringBuilder();
for (Map.Entry<String,String> e:p.entrySet())
t.append(e.getKey()).append("=").append(e.getValue()).append("\n");
return t.toString();
}
private Map<String,String> toMap(String j) {
Map<String,String> m = new HashMap<>();
for (String l:j.split("\n")) {
String s[] = l.split("=");
m.put(s[0],s[1]);
}
return m;
}
private Map<String,String> read() {
try {
return toMap(Files.readString(path));
} catch (IOException e) {
e.printStackTrace();
}
return new HashMap<>();
}
private void write(Map<String,String> p) {
try {
Files.write(path, toText(p).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

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

View File

@ -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<RecordState> 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<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,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<RecordEvent> events;
private Vec3d start_record_pos;
private Vec3d finish_record_pos;
public RecordState(File file,
String name,
Date date,
String author,
List<RecordEvent> 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<RecordEvent> 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<RecordEvent> 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<String> lines = List.of(text.split("\n"));
List<String> 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<String> event_lines = lines.subList(4,lines.size());
List<RecordEvent> 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);
}
}

View File

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

View File

@ -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<RenderListener> 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 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,94 +0,0 @@
package themixray.repeating.mod;
import java.util.ArrayList;
import java.util.List;
public abstract class TickTask implements Runnable {
public static List<TickTask> 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++;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<ServerPlayPacketListener> packet,
// BooleanSupplier sendCondition,
// Duration expirationTime,
// CallbackInfo ci) {
//
// }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,103 +1,103 @@
package themixray.repeating.mod; package themixray.repeating.mod;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.io.File; import java.io.File;
public class EasyConfig { public class EasyConfig {
public final Path path; public final Path path;
public final File file; public final File file;
public Map<String,String> data; public Map<String,String> data;
public EasyConfig(File f, Map<String,String> def) { public EasyConfig(File f, Map<String,String> def) {
this.path = f.toPath(); this.path = f.toPath();
this.file = f; this.file = f;
this.data = new HashMap<>(); this.data = new HashMap<>();
if (!file.exists()) { if (!file.exists()) {
try { try {
file.createNewFile(); file.createNewFile();
write(def); write(def);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
reload(); reload();
for (Map.Entry<String,String> m:def.entrySet()) for (Map.Entry<String,String> m:def.entrySet())
if (!data.containsKey(m.getKey())) if (!data.containsKey(m.getKey()))
data.put(m.getKey(),m.getValue()); data.put(m.getKey(),m.getValue());
save(); save();
} }
public EasyConfig(Path f, Map<String,String> def) { public EasyConfig(Path f, Map<String,String> def) {
this(f.toFile(),def); this(f.toFile(),def);
} }
public EasyConfig(String parent,String child,Map<String,String> def) { public EasyConfig(String parent,String child,Map<String,String> def) {
this(new File(parent,child),def); this(new File(parent,child),def);
} }
public EasyConfig(File parent,String child,Map<String,String> def) { public EasyConfig(File parent,String child,Map<String,String> def) {
this(new File(parent,child),def); this(new File(parent,child),def);
} }
public EasyConfig(Path parent,String child,Map<String,String> def) { public EasyConfig(Path parent,String child,Map<String,String> def) {
this(new File(parent.toFile(),child),def); this(new File(parent.toFile(),child),def);
} }
public EasyConfig(File f) { public EasyConfig(File f) {
this(f,new HashMap<>()); this(f,new HashMap<>());
} }
public EasyConfig(Path path) { public EasyConfig(Path path) {
this(path.toFile(),new HashMap<>()); this(path.toFile(),new HashMap<>());
} }
public EasyConfig(String parent,String child) { public EasyConfig(String parent,String child) {
this(new File(parent,child),new HashMap<>()); this(new File(parent,child),new HashMap<>());
} }
public EasyConfig(File parent,String child) { public EasyConfig(File parent,String child) {
this(new File(parent,child),new HashMap<>()); this(new File(parent,child),new HashMap<>());
} }
public EasyConfig(Path parent,String child) { public EasyConfig(Path parent,String child) {
this(new File(parent.toFile(),child),new HashMap<>()); this(new File(parent.toFile(),child),new HashMap<>());
} }
public void reload() { public void reload() {
data = read(); data = read();
} }
public void save() { public void save() {
write(data); write(data);
} }
private String toText(Map<String,String> p) { private String toText(Map<String,String> p) {
StringBuilder t = new StringBuilder(); StringBuilder t = new StringBuilder();
for (Map.Entry<String,String> e:p.entrySet()) for (Map.Entry<String,String> e:p.entrySet())
t.append(e.getKey()).append("=").append(e.getValue()).append("\n"); t.append(e.getKey()).append("=").append(e.getValue()).append("\n");
return t.toString(); return t.toString();
} }
private Map<String,String> toMap(String j) { private Map<String,String> toMap(String j) {
Map<String,String> m = new HashMap<>(); Map<String,String> m = new HashMap<>();
for (String l:j.split("\n")) { for (String l:j.split("\n")) {
String s[] = l.split("="); String s[] = l.split("=");
m.put(s[0],s[1]); m.put(s[0],s[1]);
} }
return m; return m;
} }
private Map<String,String> read() { private Map<String,String> read() {
try { try {
return toMap(Files.readString(path)); return toMap(Files.readString(path));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return new HashMap<>(); return new HashMap<>();
} }
private void write(Map<String,String> p) { private void write(Map<String,String> p) {
try { try {
Files.write(path, toText(p).getBytes()); Files.write(path, toText(p).getBytes());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }

View File

@ -1,339 +1,341 @@
package themixray.repeating.mod; package themixray.repeating.mod;
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;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.math.Vec3d; 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.event.events.DelayEvent; import themixray.repeating.mod.event.events.DelayEvent;
import themixray.repeating.mod.event.RecordEvent; import themixray.repeating.mod.event.RecordEvent;
import themixray.repeating.mod.event.events.InputEvent; import themixray.repeating.mod.event.events.InputEvent;
import themixray.repeating.mod.event.events.MoveEvent; import themixray.repeating.mod.event.events.MoveEvent;
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.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
public class Main 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 Main me; public static Main me;
public RecordList record_list; public RecordList record_list;
public RecordState now_record; public RecordState now_record;
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;
public TickTask replay_tick = null; public TickTask replay_tick = null;
public boolean is_replaying = false; public boolean is_replaying = false;
public boolean loop_replay = false; public boolean loop_replay = false;
public static InputEvent input_replay = null; public static InputEvent input_replay = null;
public long living_ticks = 0; public long living_ticks = 0;
public static RepeatingScreen menu; public static RepeatingScreen menu;
private static KeyBinding menu_key; private static KeyBinding menu_key;
private static KeyBinding toggle_replay_key; private static KeyBinding toggle_replay_key;
private static KeyBinding toggle_record_key; private static KeyBinding toggle_record_key;
public long record_pos_delay = 20; public long record_pos_delay = 20;
public static Random rand = new Random(); public static Random rand = new Random();
public EasyConfig conf; public EasyConfig conf;
public File records_folder; 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; now_record = null;
records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records"); records_folder = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating_mod_records");
if (!records_folder.exists()) records_folder.mkdir(); if (!records_folder.exists()) records_folder.mkdir();
record_list = new RecordList(records_folder); record_list = new RecordList(records_folder);
record_list.loadRecords(); 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 (now_record != null) { if (now_record != null) {
Vec3d start_pos = now_record.getStartRecordPos(); Vec3d start_pos = now_record.getStartRecordPos();
Vec3d finish_pos = now_record.getFinishRecordPos(); Vec3d finish_pos = now_record.getFinishRecordPos();
if (start_pos != null) drawRecordPos(buffer, start_pos, new Color(70, 230, 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)); if (finish_pos != null) drawRecordPos(buffer, finish_pos, new Color(230, 70, 70, 128));
} }
RenderHelper.endTri(buffer); RenderHelper.endTri(buffer);
}); });
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT); TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT);
}); });
Map<String,String> def = new HashMap<>(); Map<String,String> def = new HashMap<>();
def.put("record_pos_delay", String.valueOf(record_pos_delay)); def.put("record_pos_delay", String.valueOf(record_pos_delay));
conf = new 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")); record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay"));
menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.repeating-mod.menu",InputUtil.Type.KEYSYM, "key.repeating-mod.menu",InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_J,"text.repeating-mod.name")); GLFW.GLFW_KEY_J,"text.repeating-mod.name"));
toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM,
-1,"text.repeating-mod.name")); -1,"text.repeating-mod.name"));
toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM,
-1,"text.repeating-mod.name")); -1,"text.repeating-mod.name"));
menu = new RepeatingScreen(); menu = new RepeatingScreen();
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
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 (now_record != null) { if (now_record != null) {
if (!is_recording) { if (!is_recording) {
if (is_replaying) if (is_replaying)
stopReplay(); stopReplay();
else startReplay(); else startReplay();
menu.updateButtons(); menu.updateButtons();
} }
} }
} }
if (toggle_record_key.wasPressed()) { if (toggle_record_key.wasPressed()) {
if (!is_replaying) { if (!is_replaying) {
if (is_recording) if (is_recording)
stopRecording(); stopRecording();
else startRecording(); else startRecording();
menu.updateButtons(); menu.updateButtons();
} }
} }
}); });
new TickTask(0,0) { new TickTask(0,0) {
@Override @Override
public void run() { public void run() {
living_ticks++; living_ticks++;
} }
}; };
System.setProperty("java.awt.headless", "false"); System.setProperty("java.awt.headless", "false");
} }
public void setNowRecord(RecordState record) { public void setNowRecord(RecordState record) {
now_record = record; now_record = record;
} }
public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) {
RenderHelper.drawRectFromTri(buffer, RenderHelper.drawRectFromTri(buffer,
(float) pos.getX() - 0.25F, (float) pos.getX() - 0.25F,
(float) pos.getY() + 0.01F, (float) pos.getY() + 0.01F,
(float) pos.getZ() - 0.25F, (float) pos.getZ() - 0.25F,
(float) pos.getX() + 0.25F, (float) pos.getX() + 0.25F,
(float) pos.getY() + 0.01F, (float) pos.getY() + 0.01F,
(float) pos.getZ() - 0.25F, (float) pos.getZ() - 0.25F,
(float) pos.getX() + 0.25F, (float) pos.getX() + 0.25F,
(float) pos.getY() + 0.01F, (float) pos.getY() + 0.01F,
(float) pos.getZ() + 0.25F, (float) pos.getZ() + 0.25F,
(float) pos.getX() - 0.25F, (float) pos.getX() - 0.25F,
(float) pos.getY() + 0.01F, (float) pos.getY() + 0.01F,
(float) pos.getZ() + 0.25F, (float) pos.getZ() + 0.25F,
color); color);
} }
public void startRecording() { public void startRecording() {
is_recording = true; is_recording = true;
menu.updateButtons(); menu.updateButtons();
now_record = record_list.newRecord(); now_record = record_list.newRecord();
Vec3d start_pos = client.player.getPos(); Vec3d start_pos = client.player.getPos();
now_record.addEvent(new MoveEvent(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); now_record.setStartRecordPos(start_pos);
if (record_pos_delay > 0) { if (record_pos_delay > 0) {
move_tick = new TickTask( move_tick = new TickTask(
record_pos_delay, record_pos_delay,
record_pos_delay) { record_pos_delay) {
@Override @Override
public void run() { public void run() {
now_record.addEvent(new MoveEvent(client.player.getPos(), now_record.addEvent(new MoveEvent(client.player.getPos(),
client.player.getHeadYaw(), client.player.getPitch())); client.player.getHeadYaw(), client.player.getPitch()));
} }
}; };
} }
sendMessage(Text.translatable("message.repeating-mod.record_start")); sendMessage(Text.translatable("message.repeating-mod.record_start"));
} }
public void recordTick(RecordEvent e) { public void recordTick(RecordEvent e) {
if (is_recording) { if (is_recording) {
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) now_record.addEvent(new DelayEvent(diff)); if (diff > 0) now_record.addEvent(new DelayEvent(diff));
} }
now_record.addEvent(e); now_record.addEvent(e);
last_record = now; last_record = now;
} }
} }
public void recordAllInput() { public void recordAllInput() {
if (client.player == null) { if (client.player == null) {
stopRecording(); stopRecording();
return; return;
} }
InputEvent l = ((InputEvent) now_record.getLastEvent("input")); InputEvent l = ((InputEvent) now_record.getLastEvent("input"));
if (l == null) { if (l == null) {
InputEvent e = new InputEvent( InputEvent e = new InputEvent(
client.player.input.sneaking, client.player.input.sneaking,
client.player.input.jumping, client.player.input.jumping,
client.player.input.movementSideways, client.player.input.movementSideways,
client.player.input.movementForward, client.player.input.movementForward,
client.player.input.pressingForward, client.player.input.pressingForward,
client.player.input.pressingBack, client.player.input.pressingBack,
client.player.input.pressingLeft, client.player.input.pressingLeft,
client.player.input.pressingRight, client.player.input.pressingRight,
client.player.getHeadYaw(), client.player.getHeadYaw(),
client.player.getBodyYaw(), client.player.getBodyYaw(),
client.player.getPitch(), client.player.getPitch(),
client.player.isSprinting(), client.player.isSprinting(),
client.player.getYaw(), client.player.getYaw(),
client.player.getMovementSpeed()); client.player.getMovementSpeed());
recordTick(e); recordTick(e);
} else { } else {
InputEvent e = new InputEvent( InputEvent e = new InputEvent(
((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, ((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking,
((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, ((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.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways,
(((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward, (((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.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward,
((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(), Main.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());
if (!(e.isEmpty() && if (!(e.isEmpty() &&
e.yaw == l.yaw && e.yaw == l.yaw &&
e.head_yaw == l.head_yaw && e.head_yaw == l.head_yaw &&
e.pitch == l.pitch && e.pitch == l.pitch &&
e.body_yaw == l.body_yaw)) { e.body_yaw == l.body_yaw)) {
e.fillEmpty(l); e.fillEmpty(l);
recordTick(e); recordTick(e);
} }
} }
} }
public void stopRecording() { public void stopRecording() {
is_recording = false; is_recording = false;
now_record.setFinishRecordPos(client.player.getPos()); now_record.setFinishRecordPos(client.player.getPos());
try { try {
now_record.save(); now_record.save();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
if (move_tick != null) { if (move_tick != null) {
move_tick.cancel(); move_tick.cancel();
move_tick = null; move_tick = null;
} }
menu.updateButtons(); menu.updateButtons();
last_record = -1; last_record = -1;
sendMessage(Text.translatable("message.repeating-mod.record_stop")); sendMessage(Text.translatable("message.repeating-mod.record_stop"));
} }
public void startReplay() { public void startReplay() {
is_recording = false; is_recording = false;
is_replaying = true; is_replaying = true;
menu.updateButtons(); menu.updateButtons();
List<RecordEvent> events = now_record.getEvents(); List<RecordEvent> events = now_record.getEvents();
replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) { replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) {
public int replay_index = 0; public int replay_index = 0;
@Override @Override
public void run() { public void run() {
if (!is_replaying) { if (!is_replaying) {
cancel(); cancel();
return; return;
} }
RecordEvent e = events.get(replay_index); RecordEvent e = events.get(replay_index);
if (e instanceof DelayEvent) { if (e != null) {
setDelay(((DelayEvent) e).delay); if (e instanceof DelayEvent) {
} else { setDelay(((DelayEvent) e).delay);
e.replay(); } else {
} e.replay();
}
replay_index++; }
if (!loop_replay) {
if (replay_index == events.size()) { replay_index++;
stopReplay(); if (!loop_replay) {
cancel(); if (replay_index >= events.size()) {
} stopReplay();
} else if (replay_index == events.size()) { cancel();
replay_index = 0; }
} } else if (replay_index >= events.size()) {
} replay_index = 0;
}; }
}
sendMessage(Text.translatable("message.repeating-mod.replay_start")); };
}
sendMessage(Text.translatable("message.repeating-mod.replay_start"));
public void stopReplay() { }
is_recording = false;
is_replaying = false; public void stopReplay() {
if (replay_tick != null) { is_recording = false;
replay_tick.cancel(); is_replaying = false;
replay_tick = null; if (replay_tick != null) {
} replay_tick.cancel();
try { replay_tick = null;
now_record.save(); }
} catch (IOException e) { try {
throw new RuntimeException(e); now_record.save();
} } catch (IOException e) {
menu.updateButtons(); throw new RuntimeException(e);
record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start")); }
sendMessage(Text.translatable("message.repeating-mod.replay_stop")); 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")) public static void sendMessage(MutableText text) {
.append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY) client.player.sendMessage(Text.literal("[")
.append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY))); .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)));
} public static void sendDebug(String s) {
} client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s)));
}
}

View File

@ -51,6 +51,7 @@ public class RecordList {
} }
public void addRecord(RecordState record) { public void addRecord(RecordState record) {
if (record == null) return;
records.add(record); records.add(record);
widget.addWidget(record); widget.addWidget(record);
} }

View File

@ -96,6 +96,7 @@ public class RecordState {
} }
public void addEvent(RecordEvent event) { public void addEvent(RecordEvent event) {
if (event == null) return;
events.add(event); events.add(event);
} }
@ -124,9 +125,10 @@ public class RecordState {
.append(finish_record_pos.getY()).append("n") .append(finish_record_pos.getY()).append("n")
.append(finish_record_pos.getZ()); .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("\n");
text.append(events.get(i).serialize()); text.append(event.serialize());
} }
Files.write(file.toPath(), text.toString().getBytes()); Files.write(file.toPath(), text.toString().getBytes());

View File

@ -1,187 +1,187 @@
package themixray.repeating.mod; 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.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Drawable; import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.Element; import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable; 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 themixray.repeating.mod.widget.RecordListWidget; import themixray.repeating.mod.widget.RecordListWidget;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class RepeatingScreen extends Screen { public class RepeatingScreen extends Screen {
private static List<RenderListener> render_listeners = new ArrayList<>(); private static List<RenderListener> render_listeners = new ArrayList<>();
public ButtonWidget record_btn; public ButtonWidget record_btn;
public ButtonWidget loop_btn; public ButtonWidget loop_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;
protected RepeatingScreen() { protected RepeatingScreen() {
super(Text.empty()); super(Text.empty());
} }
public static void addRenderListener(RenderListener render) { public static void addRenderListener(RenderListener render) {
render_listeners.add(render); render_listeners.add(render);
} }
public static void removeRenderListener(RenderListener render) { public static void removeRenderListener(RenderListener render) {
render_listeners.remove(render); render_listeners.remove(render);
} }
public void updateButtons() { public void updateButtons() {
if (was_build) { if (was_build) {
record_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.is_recording) ? "stop_record" : "start_record"))); 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"))); loop_btn.setMessage(Text.translatable("text.repeating-mod." + ((Main.me.loop_replay) ? "off_loop" : "on_loop")));
} }
} }
@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) { for (RenderListener l : render_listeners) {
if (l.beforeRender()) { if (l.beforeRender()) {
l.render(context, mouseX, mouseY, delta); l.render(context, mouseX, mouseY, delta);
} }
} }
super.render(context, mouseX, mouseY, delta); super.render(context, mouseX, mouseY, delta);
for (RenderListener l : render_listeners) { for (RenderListener l : render_listeners) {
if (!l.beforeRender()) { if (!l.beforeRender()) {
l.render(context, mouseX, mouseY, delta); l.render(context, mouseX, mouseY, delta);
} }
} }
} }
@Override @Override
protected void init() { protected void init() {
RecordListWidget list_widget = Main.me.record_list.getWidget(); RecordListWidget list_widget = Main.me.record_list.getWidget();
list_widget.setX(width / 2 + 2); list_widget.setX(width / 2 + 2);
list_widget.setY(height / 2 - list_widget.getHeight() / 2); list_widget.setY(height / 2 - list_widget.getHeight() / 2);
list_widget.init(this); list_widget.init(this);
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 (!Main.me.is_replaying) { if (!Main.me.is_replaying) {
if (Main.me.is_recording) if (Main.me.is_recording)
Main.me.stopRecording(); Main.me.stopRecording();
else Main.me.startRecording(); else Main.me.startRecording();
updateButtons(); updateButtons();
} }
}) })
.dimensions(width / 2 - 120, height / 2 - 32, 120, 20) .dimensions(width / 2 - 120, height / 2 - 32, 120, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip"))) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip")))
.build(); .build();
loop_btn = ButtonWidget.builder(Text.empty(), button -> { loop_btn = ButtonWidget.builder(Text.empty(), button -> {
Main.me.loop_replay = !Main.me.loop_replay; Main.me.loop_replay = !Main.me.loop_replay;
updateButtons(); updateButtons();
}) })
.dimensions(width / 2 - 120, height / 2 - 10, 120, 20) .dimensions(width / 2 - 120, height / 2 - 10, 120, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip"))) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip")))
.build(); .build();
pos_delay_slider = new SliderWidget( pos_delay_slider = new SliderWidget(
width / 2 - 120, height / 2 + 12, 120, 20, width / 2 - 120, height / 2 + 12, 120, 20,
(Main.me.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(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) { (Main.me.record_pos_delay+1d)/101d) {
@Override @Override
protected void updateMessage() { protected void updateMessage() {
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)));
} }
@Override @Override
protected void applyValue() { protected void applyValue() {
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)));
Main.me.record_pos_delay = (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.data.put("record_pos_delay",String.valueOf(Main.me.record_pos_delay));
Main.me.conf.save(); Main.me.conf.save();
} }
@Override @Override
public void onRelease(double mouseX, double mouseY) { public void onRelease(double mouseX, double mouseY) {
super.onRelease(mouseX, mouseY); super.onRelease(mouseX, mouseY);
applyValue(); applyValue();
} }
@Override @Override
protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) { protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) {
super.onDrag(mouseX, mouseY, deltaX, deltaY); super.onDrag(mouseX, mouseY, deltaX, deltaY);
applyValue(); applyValue();
} }
}; };
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 -> { import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> {
new Thread(() -> { new Thread(() -> {
FileDialog fd = new FileDialog((java.awt.Frame) null); FileDialog fd = new FileDialog((java.awt.Frame) null);
fd.setMultipleMode(true); fd.setMultipleMode(true);
fd.setName("Choose record files"); fd.setName("Choose record files");
fd.setTitle("Choose record files"); fd.setTitle("Choose record files");
fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm"));
fd.setVisible(true); fd.setVisible(true);
File[] files = fd.getFiles(); File[] files = fd.getFiles();
if (files != null) { if (files != null) {
for (File file : files) { for (File file : files) {
try { try {
Main.me.setNowRecord(Main.me.record_list.cloneRecord(file)); Main.me.setNowRecord(Main.me.record_list.cloneRecord(file));
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
}}).start(); }}).start();
}) })
.dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip"))) .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip")))
.build(); .build();
was_build = true; was_build = true;
updateButtons(); updateButtons();
addDrawableChild(loop_btn); addDrawableChild(loop_btn);
addDrawableChild(record_btn); addDrawableChild(record_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) { public <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement) {
return super.addDrawableChild(drawableElement); return super.addDrawableChild(drawableElement);
} }
public <T extends Drawable> T addDrawable(T drawable) { public <T extends Drawable> T addDrawable(T drawable) {
return super.addDrawable(drawable); return super.addDrawable(drawable);
} }
public <T extends Element & Selectable> T addSelectableChild(T child) { public <T extends Element & Selectable> T addSelectableChild(T child) {
return super.addSelectableChild(child); return super.addSelectableChild(child);
} }
public void remove(Element child) { public void remove(Element child) {
super.remove(child); super.remove(child);
} }
} }

View File

@ -1,95 +1,95 @@
package themixray.repeating.mod; package themixray.repeating.mod;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public abstract class TickTask implements Runnable { public abstract class TickTask implements Runnable {
public static List<TickTask> tasks = new ArrayList<>(); public static List<TickTask> tasks = new ArrayList<>();
public static void tickTasks(TickAt at) { public static void tickTasks(TickAt at) {
for (TickTask t:new ArrayList<>(tasks)) for (TickTask t:new ArrayList<>(tasks))
if (t.getAt() == at) t.tick(); if (t.getAt() == at) t.tick();
} }
private long living; private long living;
private long delay; private long delay;
private boolean is_repeating; private boolean is_repeating;
private long period; private long period;
private boolean is_cancelled; private boolean is_cancelled;
private TickAt at; private TickAt at;
public enum TickAt { public enum TickAt {
CLIENT_HEAD, CLIENT_TAIL, CLIENT_HEAD, CLIENT_TAIL,
MOVEMENT_HEAD, MOVEMENT_TAIL, MOVEMENT_HEAD, MOVEMENT_TAIL,
RENDER_HEAD, RENDER_TAIL, RENDER_HEAD, RENDER_TAIL,
CLIENT_EVENT CLIENT_EVENT
} }
public TickTask(long delay, TickAt at) { public TickTask(long delay, TickAt at) {
this.is_cancelled = false; this.is_cancelled = false;
this.is_repeating = false; this.is_repeating = false;
this.delay = delay; this.delay = delay;
this.living = 0; this.living = 0;
this.period = 0; this.period = 0;
this.at = at; this.at = at;
tasks.add(this); tasks.add(this);
} }
public TickTask(long delay, long period, TickAt at) { public TickTask(long delay, long period, TickAt at) {
this.is_cancelled = false; this.is_cancelled = false;
this.is_repeating = true; this.is_repeating = true;
this.delay = delay; this.delay = delay;
this.period = period; this.period = period;
this.living = 0; this.living = 0;
this.at = at; this.at = at;
tasks.add(this); tasks.add(this);
} }
public TickTask(long delay) { public TickTask(long delay) {
this(delay,TickAt.CLIENT_HEAD); this(delay,TickAt.CLIENT_HEAD);
} }
public TickTask(long delay, long period) { public TickTask(long delay, long period) {
this(delay,period,TickAt.CLIENT_HEAD); this(delay,period,TickAt.CLIENT_HEAD);
} }
public void cancel() { public void cancel() {
if (!is_cancelled) { if (!is_cancelled) {
is_cancelled = true; is_cancelled = true;
tasks.remove(this); tasks.remove(this);
} }
} }
public boolean isCancelled() { public boolean isCancelled() {
return is_cancelled; return is_cancelled;
} }
public TickAt getAt() { public TickAt getAt() {
return at; return at;
} }
public void setDelay(long delay) { public void setDelay(long delay) {
if (is_repeating) { if (is_repeating) {
this.delay = delay; this.delay = delay;
} }
} }
public long getDelay() { public long getDelay() {
return this.delay; return this.delay;
} }
public void tick() { public void tick() {
if (living >= delay) { if (living >= delay) {
if (is_repeating) { if (is_repeating) {
delay = period; delay = period;
run(); run();
living = -1; living = -1;
} else { } else {
run(); run();
cancel(); cancel();
} }
} }
living++; living++;
} }
} }

View File

@ -16,7 +16,7 @@ public class GuiMouseScrollEvent extends RecordEvent {
public void replay() { public void replay() {
if (Main.client.currentScreen != null) { if (Main.client.currentScreen != null) {
Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount); // Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount);
} }
} }

View File

@ -1,24 +1,24 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
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.Main; 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 (Main.me.is_recording) if (Main.me.is_recording)
Main.me.recordAllInput(); Main.me.recordAllInput();
TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD); TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD);
} }
@Inject(at = @At(value = "TAIL"), method = "tick") @Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(CallbackInfo ci) { private void onTickTail(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL); TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL);
} }
} }

View File

@ -1,31 +1,31 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; 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.Main; import themixray.repeating.mod.Main;
import java.util.UUID; import java.util.UUID;
@Mixin(Entity.class) @Mixin(Entity.class)
public abstract class EntityMixin { public abstract class EntityMixin {
@Shadow public abstract UUID getUuid(); @Shadow public abstract UUID getUuid();
@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 (Main.client.player != null) { if (Main.client.player != null) {
if (getUuid().equals(Main.client.player.getUuid())) { if (getUuid().equals(Main.client.player.getUuid())) {
if (Main.me.is_replaying) { if (Main.me.is_replaying) {
if (Main.input_replay != null && if (Main.input_replay != null &&
Main.input_replay.sprinting != null && Main.input_replay.sprinting != null &&
Main.input_replay.sprinting != sprinting) { Main.input_replay.sprinting != sprinting) {
ci.cancel(); ci.cancel();
} }
} }
} }
} }
} }
} }

View File

@ -1,20 +1,20 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.minecraft.client.input.KeyboardInput; import net.minecraft.client.input.KeyboardInput;
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.Main; 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 (Main.me.is_replaying) { if (Main.me.is_replaying) {
if (Main.input_replay != null) { if (Main.input_replay != null) {
Main.input_replay.inputCallback(); Main.input_replay.inputCallback();
} }
} }
} }
} }

View File

@ -1,44 +1,44 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
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.Main; import themixray.repeating.mod.Main;
import themixray.repeating.mod.event.events.BlockBreakEvent; import themixray.repeating.mod.event.events.BlockBreakEvent;
import themixray.repeating.mod.event.events.BlockInteractEvent; import themixray.repeating.mod.event.events.BlockInteractEvent;
import themixray.repeating.mod.TickTask; import themixray.repeating.mod.TickTask;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
public abstract class MovementMixin { 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 (Main.me.is_recording) if (Main.me.is_recording)
Main.me.recordTick(new BlockBreakEvent(pos)); Main.me.recordTick(new BlockBreakEvent(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 (Main.me.is_recording) if (Main.me.is_recording)
Main.me.recordTick(new BlockInteractEvent(hand,hitResult)); Main.me.recordTick(new BlockInteractEvent(hand,hitResult));
return ActionResult.PASS; return ActionResult.PASS;
}); });
} }
@Inject(at = @At(value = "HEAD"), method = "tickMovement") @Inject(at = @At(value = "HEAD"), method = "tickMovement")
private void onMoveHead(CallbackInfo ci) { private void onMoveHead(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD); TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD);
} }
@Inject(at = @At(value = "TAIL"), method = "tick") @Inject(at = @At(value = "TAIL"), method = "tick")
private void onMoveTail(CallbackInfo ci) { private void onMoveTail(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL); TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL);
} }
} }

View File

@ -1,29 +1,29 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
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;
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 java.time.Duration; import java.time.Duration;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
@Mixin(ClientPlayNetworkHandler.class) @Mixin(ClientPlayNetworkHandler.class)
public abstract class NetworkMixin { public abstract class NetworkMixin {
// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V") // @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;)V")
// private void onSendPacket1Head(Packet<?> packet, // private void onSendPacket1Head(Packet<?> packet,
// CallbackInfo ci) { // CallbackInfo ci) {
// //
// } // }
// //
// @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V") // @Inject(at = @At(value = "HEAD"), method = "sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V")
// private void onSendPacket2Head(Packet<ServerPlayPacketListener> packet, // private void onSendPacket2Head(Packet<ServerPlayPacketListener> packet,
// BooleanSupplier sendCondition, // BooleanSupplier sendCondition,
// Duration expirationTime, // Duration expirationTime,
// CallbackInfo ci) { // CallbackInfo ci) {
// //
// } // }
} }

View File

@ -1,21 +1,21 @@
package themixray.repeating.mod.mixin; package themixray.repeating.mod.mixin;
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.TickTask; import themixray.repeating.mod.TickTask;
@Mixin(GameRenderer.class) @Mixin(GameRenderer.class)
public abstract class RendererMixin { public abstract class RendererMixin {
@Inject(at = @At(value = "HEAD"), method = "tick") @Inject(at = @At(value = "HEAD"), method = "tick")
private void onTickHead(CallbackInfo ci) { private void onTickHead(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD); TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD);
} }
@Inject(at = @At(value = "TAIL"), method = "tick") @Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(CallbackInfo ci) { private void onTickTail(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL); TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL);
} }
} }

View File

@ -80,11 +80,11 @@ public abstract class ScreenMixin extends AbstractParentElement implements Drawa
return super.mouseReleased(mouseX, mouseY, button); return super.mouseReleased(mouseX, mouseY, button);
} }
@Override // @Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) { // public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
if (Main.me.is_recording) { // 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); // return super.mouseScrolled(mouseX, mouseY, amount);
} // }
} }

View File

@ -134,7 +134,8 @@ public class RecordWidget implements Drawable, Widget {
children.add(export_button); 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) { if (Main.me.is_replaying) {
Main.me.stopReplay(); Main.me.stopReplay();
if (getRecord().equals(Main.me.now_record)) { 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")); i.setMessage(Text.translatable("text.repeating-mod.stop"));
Main.me.now_record = record; Main.me.now_record = getRecord();
Main.me.startReplay(); Main.me.startReplay();
Main.client.setScreen(null); Main.client.setScreen(null);
}).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13)

View File

@ -1,17 +1,17 @@
{ {
"providers": [ "providers": [
{ {
"file":"repeating-mod:ui/no-loop.png", "file":"repeating-mod:ui/no-loop.png",
"chars":["\ueffe"], "chars":["\ueffe"],
"height":16, "height":16,
"ascent":12, "ascent":12,
"type":"bitmap" "type":"bitmap"
},{ },{
"file":"repeating-mod:ui/loop.png", "file":"repeating-mod:ui/loop.png",
"chars":["\uefff"], "chars":["\uefff"],
"height":16, "height":16,
"ascent":12, "ascent":12,
"type":"bitmap" "type":"bitmap"
} }
] ]
} }

View File

@ -1,36 +1,36 @@
{ {
"key.repeating-mod.menu": "Repeating menu", "key.repeating-mod.menu": "Repeating menu",
"key.repeating-mod.toggle_replay": "Toggle replay", "key.repeating-mod.toggle_replay": "Toggle replay",
"key.repeating-mod.toggle_record": "Toggle recording", "key.repeating-mod.toggle_record": "Toggle recording",
"text.repeating-mod.name": "Repeating Mod", "text.repeating-mod.name": "Repeating Mod",
"text.repeating-mod.start_record": "Start record", "text.repeating-mod.start_record": "Start record",
"text.repeating-mod.stop_record": "Stop record", "text.repeating-mod.stop_record": "Stop record",
"text.repeating-mod.start_replay": "Start replay", "text.repeating-mod.start_replay": "Start replay",
"text.repeating-mod.stop_replay": "Stop replay", "text.repeating-mod.stop_replay": "Stop replay",
"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_record": "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 a recording from a 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.unnamed": "Unnamed Record",
"text.repeating-mod.on_loop": "Enable repeat", "text.repeating-mod.on_loop": "Enable repeat",
"text.repeating-mod.off_loop": "Disable repeat", "text.repeating-mod.off_loop": "Disable repeat",
"text.repeating-mod.recorded_at": "Recorded at", "text.repeating-mod.recorded_at": "Recorded at",
"text.repeating-mod.author": "Author", "text.repeating-mod.author": "Author",
"text.repeating-mod.delete": "Delete", "text.repeating-mod.delete": "Delete",
"text.repeating-mod.start": "Start", "text.repeating-mod.start": "Start",
"text.repeating-mod.stop": "Stop", "text.repeating-mod.stop": "Stop",
"text.repeating-mod.export": "Export", "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",
"message.repeating-mod.record_start": "Record started", "message.repeating-mod.record_start": "Record started",
"message.repeating-mod.record_stop": "Record finished" "message.repeating-mod.record_stop": "Record finished"
} }

View File

@ -1,38 +1,38 @@
{ {
"key.repeating-mod.menu": "Меню репитинга", "key.repeating-mod.menu": "Меню репитинга",
"key.repeating-mod.toggle_replay": "Вкл/выкл повтор", "key.repeating-mod.toggle_replay": "Вкл/выкл повтор",
"key.repeating-mod.toggle_record": "Вкл/выкл запись", "key.repeating-mod.toggle_record": "Вкл/выкл запись",
"text.repeating-mod.name": "Репитинг Мод", "text.repeating-mod.name": "Репитинг Мод",
"text.repeating-mod.start_record": "Начать запись", "text.repeating-mod.start_record": "Начать запись",
"text.repeating-mod.stop_record": "Остановить запись", "text.repeating-mod.stop_record": "Остановить запись",
"text.repeating-mod.start_replay": "Начать повтор", "text.repeating-mod.start_replay": "Начать повтор",
"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.import": "Импорт записи", "text.repeating-mod.import": "Импорт записи",
"text.repeating-mod.export_tooltip": "Экспорт записи в файл", "text.repeating-mod.export_tooltip": "Экспорт записи в файл",
"text.repeating-mod.import_tooltip": "Импорт записи из файла", "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.unnamed": "Безымянная Запись",
"text.repeating-mod.on_loop": "Включить повторение", "text.repeating-mod.on_loop": "Включить повторение",
"text.repeating-mod.off_loop": "Выключить повторение", "text.repeating-mod.off_loop": "Выключить повторение",
"text.repeating-mod.recorded_at": "Записано в", "text.repeating-mod.recorded_at": "Записано в",
"text.repeating-mod.author": "Автор", "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_record": "Экпорт записи",
"text.repeating-mod.export": "Экспорт", "text.repeating-mod.export": "Экспорт",
"text.repeating-mod.delete": "Удалить", "text.repeating-mod.delete": "Удалить",
"text.repeating-mod.start": "Старт", "text.repeating-mod.start": "Старт",
"text.repeating-mod.stop": "Стоп" "text.repeating-mod.stop": "Стоп"
} }

View File

@ -1,38 +1,38 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "repeating-mod", "id": "repeating-mod",
"version": "${version}", "version": "${version}",
"name": "Repeating Mod", "name": "Repeating Mod",
"description": "Mod that repeats your recorded actions. ", "description": "Mod that repeats your recorded actions. ",
"authors": [ "authors": [
"TheMixRay" "TheMixRay"
], ],
"contact": { "contact": {
"homepage": "https://modrinth.com/mod/repeating-mod", "homepage": "https://modrinth.com/mod/repeating-mod",
"sources": "https://github.com/MeexReay/repeating-mod" "sources": "https://github.com/MeexReay/repeating-mod"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
"icon": "icon.png", "icon": "icon.png",
"environment": "client", "environment": "client",
"entrypoints": { "entrypoints": {
"client": [ "client": [
"themixray.repeating.mod.Main" "themixray.repeating.mod.Main"
] ]
}, },
"mixins": [ "mixins": [
"repeating-mod.mixins.json" "repeating-mod.mixins.json"
], ],
"depends": { "depends": {
"fabricloader": ">=0.14.14", "fabricloader": ">=0.14.14",
"fabric-api": "*", "fabric-api": "*",
"minecraft": ">=1.20", "minecraft": ">=1.20",
"java": ">=17" "java": ">=17"
}, },
"suggests": { "suggests": {
"another-mod": "*" "another-mod": "*"
} }
} }

View File

@ -1,20 +1,20 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "themixray.repeating.mod.mixin", "package": "themixray.repeating.mod.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"mixins": [ "mixins": [
], ],
"client": [ "client": [
"MovementMixin", "MovementMixin",
"InputMixin", "InputMixin",
"RendererMixin", "RendererMixin",
"EntityMixin", "EntityMixin",
"ClientMixin", "ClientMixin",
"ScreenMixin", "ScreenMixin",
"PlayerMixin" "PlayerMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
} }