Compare commits

...

67 commits

Author SHA1 Message Date
654589355d fix version name 2025-06-18 18:18:55 +03:00
0b3bac264f port to 1.21.6 2025-06-18 18:17:57 +03:00
59a2175fb2 port to 1.21.5 2025-06-18 16:18:48 +03:00
ac1ee8ec6d fix record list widget 2025-06-18 15:57:29 +03:00
c65e906500 port to 1.21.4, RECORDS LIST IS BROKEN 2025-06-15 20:36:55 +03:00
f082788406 set fabric mod json 1.21. 2025-06-15 20:06:31 +03:00
072efb64f6 Merge branch '1.21' 2025-06-15 19:59:44 +03:00
06634a8704 set license wtfpl in mod 2025-06-15 19:59:29 +03:00
d101b11c36 fix movements 1.21.2 2025-06-15 16:23:04 +03:00
412c3f47ab port to 1.21.2, MOVEMENTS ARE BROKEN 2025-06-14 23:46:18 +03:00
9d7ac6b213 Merge pull request 'add "compatible with" comment' (#1) from 1.21 into master
Reviewed-on: MeexReay/repeating_mod#1
2025-06-14 22:23:13 +03:00
4714b1dbfb add compatible with comment 2025-06-14 22:22:27 +03:00
5c9df13d15 fix gitea actions 2025-06-14 21:38:30 +03:00
38127eb69f make img folder for readme images 2025-06-14 20:35:23 +03:00
5d5e628cdf add dev artifacts 2025-06-14 20:18:04 +03:00
42bf96701e Merge branch 'master' of https://git.meex.lol/MeexReay/repeating_mod 2025-06-14 20:05:07 +03:00
666377e5ee fix pos event 2025-06-14 20:04:53 +03:00
63132684cd Update README.md 2025-06-14 19:28:24 +03:00
d4abb39c80 downgrade upload-artifact 2025-06-14 08:01:57 +03:00
2e62b69a21 action refactor 2025-06-14 07:45:08 +03:00
62dcb75960 fix upload artifact 2025-06-14 06:48:00 +03:00
b6084063a1 make gradlew exec 2025-06-14 06:23:35 +03:00
1e4f4fdaeb gitea actions fix x3 2025-06-14 06:00:20 +03:00
f49b1e21a3 maybefix gitea actions 2025-06-14 05:23:49 +03:00
759b6cb05e gitea action 2025-06-14 05:17:53 +03:00
2944395e54 remove some crap 2025-06-14 05:09:39 +03:00
45998bbe2e rename group-id and artifact-id, fix recording points render 2025-06-14 05:02:06 +03:00
8242b2c601 readme update 2025-06-14 03:13:26 +03:00
e7abf3e15b open source! 2025-06-14 03:04:16 +03:00
61c755aabc move to 1.21 and fix for nixos 2025-06-14 03:02:04 +03:00
c02d96edca Merge branch 'main' of https://git.meex.lol/MeexReay/repeating_mod 2024-12-08 02:40:33 +03:00
cc12eab006 wh 2024-12-08 02:39:49 +03:00
MeexReay
41706ade80
Update README.md 2024-04-26 20:08:14 +03:00
5d9b27cd9a Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-26 17:54:52 +03:00
73154eff2b sort records by last modified + fix errors 2024-04-26 17:54:43 +03:00
MeexReay
1ceea8bcd4
Update README.md 2024-04-23 20:44:56 +03:00
ed998cc701 Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-23 20:44:02 +03:00
e808b6ca09 import now copies file to your record folder 2024-04-23 20:43:49 +03:00
MeexReay
e5981e6e60
Update README.md 2024-04-23 20:38:34 +03:00
a345f65797 Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-23 20:37:44 +03:00
8d47394acc gui not working, but im tried 2024-04-23 20:37:36 +03:00
MeexReay
5fa8fba00d
Update README.md 2024-04-23 20:23:06 +03:00
MeexReay
8a6e680ff9
Update README.md 2024-04-23 19:20:14 +03:00
f38629a294 events refactor + mixin testing 2024-04-23 19:18:22 +03:00
e76c3dfccf events refactor + mixin testing 2024-04-23 19:18:14 +03:00
28b4265a55 license 2024-04-23 15:17:39 +03:00
a255e4b804 license 2024-04-23 15:15:36 +03:00
aad9a07484 license 2024-04-23 15:13:55 +03:00
278561d374 Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-23 15:07:45 +03:00
ffc4423b1f license 2024-04-23 15:07:37 +03:00
MeexReay
26496ca3f2
Update README.md 2024-04-23 14:46:56 +03:00
84eb4e026f Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-23 03:55:44 +03:00
9596fedb6a license 2024-04-23 03:55:36 +03:00
MeexReay
50927fd2f7
Update README.md 2024-04-23 03:48:40 +03:00
MeexReay
99c057c692
Update README.md 2024-04-23 03:47:53 +03:00
e80ee84442 Merge branch 'main' of https://github.com/MeexReay/repeating-mod 2024-04-22 20:08:43 +03:00
MeexReay
74c061c29f
Update README.md 2024-04-22 20:08:26 +03:00
b162304a0b change icon to new one 2024-04-22 20:08:14 +03:00
MeexReay
3e96a7b360
Update README.md 2024-04-22 20:06:43 +03:00
596ca3c0d3 some fixes 2024-04-22 20:06:18 +03:00
MeexReay
70e97ecf92
Update README.md 2024-04-22 19:38:08 +03:00
MeexReay
e194d7f204
Update README.md 2024-04-22 18:55:50 +03:00
MeexReay
3f3ce10d7f
Update README.md 2024-04-22 18:52:44 +03:00
MeexReay
f603f5ad27
Update README.md 2024-04-22 18:50:46 +03:00
MeexReay
a1e4cb0c8d
Update README.md 2024-04-22 18:43:20 +03:00
27627e8404 1.20.4 compatibility 2024-04-22 18:17:31 +03:00
c5132221ae 1.20.4 compatibility 2024-04-22 18:17:22 +03:00
67 changed files with 2230 additions and 1562 deletions

29
.gitea/workflows/java.yml Normal file
View file

@ -0,0 +1,29 @@
on: [ push ]
name: Build fabric mod
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle 8.12.1
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582
with:
gradle-version: '8.12.1'
- name: Build with Gradle Wrapper
run: chmod +x ./gradlew; ./gradlew build
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build
path: build/libs/*

42
.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run/
# java
hs_err_*.log
replay_*.log
*.hprof
*.jfr
remappedSrc/

View file

@ -10,4 +10,4 @@ as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO. 0. You just DO WHAT THE FUCK YOU WANT TO.

View file

@ -2,16 +2,49 @@
This mod can record your movements and play them back. This mod can record your movements and play them back.
![Preview gif](preview.gif) ## How to use
## Controls Simply start recording, perform any actions, stop the recording, return to the starting point, and then you can replay everything you did.
### Showcase
![Preview gif](img/preview.gif)
This is how menu looks like:
![repeating mod menu](img/menu.png)
### Controls
Default Repeating Mod keys
``` ```
Menu | J Menu | J
Toggle recording | not specified Toggle recording | not specified by default
Toggle replay | not specified Toggle replay | not specified by default
``` ```
## Menu ## Where to download
![Repeating menu](https://github.com/MeexReay/repeating-mod/assets/127148610/da923fe5-d44d-421b-b601-2a65cb5543eb) ### Stable releases
You can find stable releases on [Modrinth](https://modrinth.com/mod/repeating-mod/versions) or on [Releases page](https://git.meex.lol/MeexReay/repeating_mod/releases)
### Development artifacts
Artifacts are automatically built with [Gitea Actions](https://git.meex.lol/MeexReay/repeating_mod/actions) on each commit. \
[Download latest artifact](https://git.meex.lol/MeexReay/repeating_mod/actions/runs/latest/artifacts/build)
## Roadmap
- [ ] relative mode for repeating actions (like mining)
- [ ] record mouse and keyboard in gui
- [ ] practice mode (like in geometry dash but for parkours)
### Contributing
If you would like to contribute to the project, feel free to fork the repository and submit a pull request.
### License
This project is licensed under the WTFPL License

View file

@ -1,24 +1,26 @@
plugins { plugins {
id 'fabric-loom' version '1.1-SNAPSHOT' id 'fabric-loom' version "${loom_version}"
id 'maven-publish' id 'maven-publish'
} }
version = project.mod_version version = project.mod_version
group = project.maven_group group = project.maven_group
base {
archivesName = project.archives_base_name
}
repositories { repositories {
// Add repositories to retrieve artifacts from in here. // Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because // You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html // See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories. // for more information about repositories.
maven { url 'https://maven.wispforest.io' }
mavenCentral()
} }
dependencies { dependencies {
compileOnly 'org.projectlombok:lombok:1.18.24' compileOnly 'org.projectlombok:lombok:1.18.38'
annotationProcessor 'org.projectlombok:lombok:1.18.24' annotationProcessor 'org.projectlombok:lombok:1.18.38'
//add joml //add joml
modImplementation 'org.joml:joml:1.10.4' modImplementation 'org.joml:joml:1.10.4'
@ -31,23 +33,6 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway. // Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// Uncomment the following line to enable the deprecated Fabric API modules.
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
// modImplementation "io.wispforest:owo-lib:${project.owo_version}"
// // only if you plan to use owo-config
// annotationProcessor "io.wispforest:owo-lib:${project.owo_version}"
// include this if you don't want force your users to install owo
// sentinel will warn them and give the option to download it automatically
// include "io.wispforest:owo-sentinel:${project.owo_version}"
}
base {
archivesName = project.archives_base_name
} }
processResources { processResources {
@ -59,8 +44,7 @@ processResources {
} }
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
// Minecraft 1.18 (1.18-pre2) upwards uses Java 17. it.options.release = 21
it.options.release = 17
} }
java { java {
@ -69,20 +53,21 @@ java {
// If you remove this line, sources will not be generated. // If you remove this line, sources will not be generated.
withSourcesJar() withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_21
} }
jar { jar {
from("LICENSE") { from("LICENSE") {
rename { "${it}_${base.archivesName.get()}"} rename { "${it}_${project.base.archivesName.get()}"}
} }
} }
// configure the maven publication // configure the maven publication
publishing { publishing {
publications { publications {
mavenJava(MavenPublication) { create("mavenJava", MavenPublication) {
artifactId = project.archives_base_name
from components.java from components.java
} }
} }
@ -94,4 +79,4 @@ publishing {
// The repositories here will be used for publishing your artifact, not for // The repositories here will be used for publishing your artifact, not for
// retrieving dependencies. // retrieving dependencies.
} }
} }

View file

@ -1,17 +1,20 @@
# 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.1 minecraft_version=1.21.6
yarn_mappings=1.20.1+build.10 yarn_mappings=1.21.6+build.1
loader_version=0.15.10 loader_version=0.16.14
loom_version=1.10-SNAPSHOT
#Fabric api
fabric_version=0.92.1+1.20.1 # Fabric API
fabric_version=0.127.0+1.21.6
# Mod Properties
mod_version = 1.1.0 # Mod Properties
maven_group = themixray.repeating.mod mod_version = 1.1.2+1.21.6
archives_base_name = repeating-mod maven_group = ru.themixray
archives_base_name = repeating-mod
# Compatible with: 1.21.6

Binary file not shown.

View file

@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

29
gradlew vendored Executable file → Normal file
View file

@ -83,10 +83,8 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -133,10 +131,13 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045 # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

20
gradlew.bat vendored
View file

@ -43,11 +43,11 @@ 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. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail

BIN
img/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View file

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

Before After
Before After

View file

@ -1,10 +1,10 @@
pluginManagement { pluginManagement {
repositories { repositories {
maven { maven {
name = 'Fabric' name = 'Fabric'
url = 'https://maven.fabricmc.net/' url = 'https://maven.fabricmc.net/'
} }
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
} }
} }

6
shell.nix Normal file
View file

@ -0,0 +1,6 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
shellHook = ''
export LD_LIBRARY_PATH="''${LD_LIBRARY_PATH}''${LD_LIBRARY_PATH:+:}${pkgs.libglvnd}/lib"
'';
}

View file

@ -1,103 +1,103 @@
package themixray.repeating.mod; package ru.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,326 +1,309 @@
package themixray.repeating.mod; package ru.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.RecordDelayEvent; import ru.themixray.repeating_mod.event.events.DelayEvent;
import themixray.repeating.mod.event.RecordEvent; import ru.themixray.repeating_mod.event.RecordEvent;
import themixray.repeating.mod.event.RecordInputEvent; import ru.themixray.repeating_mod.event.events.InputEvent;
import themixray.repeating.mod.event.RecordMoveEvent; import ru.themixray.repeating_mod.event.events.MoveEvent;
import themixray.repeating.mod.render.RenderHelper; import ru.themixray.repeating_mod.render.RenderHelper;
import themixray.repeating.mod.render.RenderSystem; import ru.themixray.repeating_mod.render.RenderSystem;
import themixray.repeating.mod.render.buffer.WorldBuffer; import ru.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 RecordInputEvent 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 = -1;
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.AFTER_ENTITIES.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);
}); });
Map<String,String> def = new HashMap<>(); ClientTickEvents.END_CLIENT_TICK.register(client -> {
def.put("record_pos_delay", String.valueOf(record_pos_delay)); TickTask.tickTasks(TickTask.TickAt.CLIENT_EVENT);
});
conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def);
Map<String,String> def = new HashMap<>();
record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay")); def.put("record_pos_delay", String.valueOf(record_pos_delay));
menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def);
"key.repeating-mod.menu",InputUtil.Type.KEYSYM,
GLFW.GLFW_KEY_J,"text.repeating-mod.name")); record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay"));
toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM, menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
-1,"text.repeating-mod.name")); "key.repeating-mod.menu",InputUtil.Type.KEYSYM,
toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( GLFW.GLFW_KEY_J,"text.repeating-mod.name"));
"key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM, toggle_replay_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
-1,"text.repeating-mod.name")); "key.repeating-mod.toggle_replay",InputUtil.Type.KEYSYM,
-1,"text.repeating-mod.name"));
menu = new RepeatingScreen(); toggle_record_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
ClientTickEvents.END_CLIENT_TICK.register(client -> { "key.repeating-mod.toggle_record",InputUtil.Type.KEYSYM,
if (menu_key.wasPressed()) -1,"text.repeating-mod.name"));
client.setScreen(menu);
if (toggle_replay_key.wasPressed()) { menu = new RepeatingScreen();
if (now_record != null) { ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (!is_recording) { if (menu_key.wasPressed())
if (is_replaying) client.setScreen(menu);
stopReplay(); if (toggle_replay_key.wasPressed()) {
else startReplay(); if (now_record != null) {
menu.updateButtons(); if (!is_recording) {
} if (is_replaying)
} stopReplay();
} else startReplay();
if (toggle_record_key.wasPressed()) { menu.updateButtons();
if (!is_replaying) { }
if (is_recording) }
stopRecording(); }
else startRecording(); if (toggle_record_key.wasPressed()) {
menu.updateButtons(); if (!is_replaying) {
} if (is_recording)
} stopRecording();
}); else startRecording();
menu.updateButtons();
new TickTask(0,0) { }
@Override }
public void run() { });
living_ticks++;
} new TickTask(0,0) {
}; @Override
public void run() {
System.setProperty("java.awt.headless", "false"); living_ticks++;
} }
};
public void setNowRecord(RecordState record) {
now_record = record; System.setProperty("java.awt.headless", "false");
} }
public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) { public void setNowRecord(RecordState record) {
RenderHelper.drawRectFromTri(buffer, now_record = record;
(float) pos.getX() - 0.25F, }
(float) pos.getY() + 0.01F,
(float) pos.getZ() - 0.25F, public void drawRecordPos(WorldBuffer buffer, Vec3d pos, Color color) {
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,
color);
} (float) pos.getX() - 0.25F,
(float) pos.getY() + 0.01F,
public void startRecording() { (float) pos.getZ() + 0.25F,
is_recording = true; color);
menu.updateButtons(); }
now_record = record_list.newRecord(); public void startRecording() {
is_recording = true;
Vec3d start_pos = client.player.getPos(); menu.updateButtons();
now_record.addEvent(new RecordMoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch()));
now_record.setStartRecordPos(start_pos); now_record = record_list.newRecord();
if (record_pos_delay > 0) { Vec3d start_pos = client.player.getPos();
move_tick = new TickTask( recordTick(new MoveEvent(start_pos,client.player.getHeadYaw(),client.player.getPitch()));
record_pos_delay, now_record.setStartRecordPos(start_pos);
record_pos_delay) {
@Override if (record_pos_delay > 0) {
public void run() { move_tick = new TickTask(
now_record.addEvent(new RecordMoveEvent(client.player.getPos(), record_pos_delay,
client.player.getHeadYaw(), client.player.getPitch())); record_pos_delay) {
} @Override
}; public void run() {
} recordTick(new MoveEvent(client.player.getPos(),
client.player.getHeadYaw(), client.player.getPitch()));
sendMessage(Text.translatable("message.repeating-mod.record_start")); }
} };
}
public void recordTick(RecordEvent e) {
if (is_recording) { sendMessage(Text.translatable("message.repeating-mod.record_start"));
long now = living_ticks; }
if (last_record != -1) {
long diff = now - last_record - 2; public void recordTick(RecordEvent e) {
if (diff > 0) now_record.addEvent(new RecordDelayEvent(diff)); if (is_recording) {
} long now = living_ticks;
now_record.addEvent(e); if (last_record != -1) {
last_record = now; long diff = now - last_record - 2;
} if (diff > 0) now_record.addEvent(new DelayEvent(diff));
} }
now_record.addEvent(e);
public void recordAllInput() { last_record = now;
if (client.player == null) { }
stopRecording(); }
return;
} public void recordAllInput() {
if (client.player == null) {
RecordInputEvent l = ((RecordInputEvent) now_record.getLastEvent("input")); stopRecording();
if (l == null) { return;
RecordInputEvent e = new RecordInputEvent( }
client.player.input.sneaking,
client.player.input.jumping, InputEvent curr = InputEvent.current();
client.player.input.movementSideways, if (curr == null) return;
client.player.input.movementForward,
client.player.input.pressingForward, InputEvent last = ((InputEvent) now_record.getLastEvent("input"));
client.player.input.pressingBack, if (last == null) {
client.player.input.pressingLeft, recordTick(curr);
client.player.input.pressingRight, } else if (!curr.equals(last)) {
client.player.getHeadYaw(), recordTick(curr.differs(last));
client.player.getBodyYaw(), }
client.player.getPitch(), }
client.player.isSprinting(),
client.player.getYaw(), public void stopRecording() {
client.player.getMovementSpeed()); is_recording = false;
recordTick(e); now_record.setFinishRecordPos(client.player.getPos());
} else { try {
RecordInputEvent e = new RecordInputEvent( now_record.save();
((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking, } catch (IOException e) {
((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping, throw new RuntimeException(e);
(((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, if (move_tick != null) {
((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward, move_tick.cancel();
((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack, move_tick = null;
((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft, }
((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight, menu.updateButtons();
client.player.getHeadYaw(), Main.client.player.getBodyYaw(),client.player.getPitch(), last_record = -1;
((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(), sendMessage(Text.translatable("message.repeating-mod.record_stop"));
client.player.getYaw(),client.player.getMovementSpeed()); }
if (!(e.isEmpty() &&
e.yaw == l.yaw && public void startReplay() {
e.head_yaw == l.head_yaw && is_recording = false;
e.pitch == l.pitch && is_replaying = true;
e.body_yaw == l.body_yaw)) { menu.updateButtons();
e.fillEmpty(l);
recordTick(e); List<RecordEvent> events = now_record.getEvents();
}
} replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_EVENT) {
} public int replay_index = 0;
public void stopRecording() { @Override
is_recording = false; public void run() {
now_record.setFinishRecordPos(client.player.getPos()); if (!is_replaying) {
try { cancel();
now_record.save(); return;
} catch (IOException e) { }
throw new RuntimeException(e);
} RecordEvent e = events.get(replay_index);
if (move_tick != null) { if (e != null) {
move_tick.cancel(); if (e instanceof DelayEvent) {
move_tick = null; setDelay(((DelayEvent) e).delay);
} } else {
menu.updateButtons(); e.replay();
last_record = -1; }
sendMessage(Text.translatable("message.repeating-mod.record_stop")); }
}
replay_index++;
if (!loop_replay) {
public void startReplay() { if (replay_index >= events.size()) {
is_recording = false; stopReplay();
is_replaying = true; cancel();
menu.updateButtons(); }
} else if (replay_index >= events.size()) {
List<RecordEvent> events = now_record.getEvents(); replay_index = 0;
}
replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) { }
public int replay_index = 0; };
@Override sendMessage(Text.translatable("message.repeating-mod.replay_start"));
public void run() { }
if (!is_replaying) cancel();
RecordEvent e = events.get(replay_index); public void stopReplay() {
if (e instanceof RecordDelayEvent) { is_recording = false;
setDelay(((RecordDelayEvent) e).delay); is_replaying = false;
} else { if (replay_tick != null) {
e.replay(); replay_tick.cancel();
} replay_tick = null;
}
replay_index++; try {
if (!loop_replay) { now_record.save();
if (replay_index == events.size()) { } catch (IOException e) {
stopReplay(); throw new RuntimeException(e);
cancel(); }
} menu.updateButtons();
} else if (replay_index == events.size()) { record_list.getWidget().getWidget(now_record).getChildren().get(3).setMessage(Text.translatable("text.repeating-mod.start"));
replay_index = 0; sendMessage(Text.translatable("message.repeating-mod.replay_stop"));
} }
}
}; public static void sendMessage(MutableText text) {
client.player.sendMessage(Text.literal("[")
sendMessage(Text.translatable("message.repeating-mod.replay_start")); .append(Text.translatable("text.repeating-mod.name"))
} .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY)
.append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY)), false);
public void stopReplay() { }
is_recording = false;
is_replaying = false; // public static void sendDebug(String s) {
if (replay_tick != null) { // client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s)), false);
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,23 +1,19 @@
package themixray.repeating.mod; package ru.themixray.repeating_mod;
import net.minecraft.text.Text; import ru.themixray.repeating_mod.widget.RecordListWidget;
import themixray.repeating.mod.widget.RecordListWidget;
import java.io.File; import java.io.File;
import java.io.IOException; import java.nio.file.Files;
import java.text.SimpleDateFormat; import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class RecordList { public class RecordList {
private final File folder; private final File folder;
private List<RecordState> records; private LinkedList<RecordState> records;
private RecordListWidget widget; private RecordListWidget widget;
public RecordList(File folder) { public RecordList(File folder) {
this.folder = folder; this.folder = folder;
this.records = new ArrayList<>(); this.records = new LinkedList<>();
this.widget = new RecordListWidget(0, 0, 180, 200); this.widget = new RecordListWidget(0, 0, 180, 200);
} }
@ -34,18 +30,25 @@ public class RecordList {
} }
public void loadRecords() { public void loadRecords() {
for (File file : folder.listFiles()) { LinkedList<File> files = new LinkedList<>(List.of(folder.listFiles()));
files.sort(Comparator.comparingLong((f) -> f.lastModified()));
for (File file : files) {
try { try {
addRecord(file); addRecord(file);
} catch (Exception e) {} } catch (Exception e) {}
} }
} }
public void addRecord(File file) throws Exception { public RecordState addRecord(File file) throws Exception {
addRecord(RecordState.load(file)); RecordState st = RecordState.load(file);
addRecord(st);
return st;
} }
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);
} }
@ -74,4 +77,12 @@ public class RecordList {
return state; return state;
} }
public RecordState cloneRecord(File file) throws Exception {
File out = new File(Main.me.records_folder, file.getName());
Files.copy(file.toPath(), out.toPath());
RecordState state = RecordState.load(out);
addRecord(state);
return state;
}
} }

View file

@ -1,10 +1,9 @@
package themixray.repeating.mod; package ru.themixray.repeating_mod;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import themixray.repeating.mod.event.RecordEvent; import ru.themixray.repeating_mod.event.RecordEvent;
import java.awt.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -96,12 +95,13 @@ public class RecordState {
} }
public void addEvent(RecordEvent event) { public void addEvent(RecordEvent event) {
if (event == null) return;
events.add(event); events.add(event);
} }
public RecordEvent getLastEvent(String type) { public RecordEvent getLastEvent(String type) {
for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) { for (RecordEvent r: Lists.reverse(new ArrayList<>(events))) {
if (r.getType().equals(type)) { if (r.getType() != null && r.getType().getName().equals(type)) {
return r; return r;
} }
} }
@ -109,6 +109,8 @@ public class RecordState {
} }
public void save() throws IOException { public void save() throws IOException {
if (start_record_pos == null || finish_record_pos == null) return;
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
text.append(name).append("\n") text.append(name).append("\n")
@ -122,9 +124,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());
@ -162,8 +165,13 @@ public class RecordState {
} }
public void remove() { public void remove() {
file.delete();
Main.me.record_list.removeRecord(this); Main.me.record_list.removeRecord(this);
Main.me.record_list.getWidget().removeWidget(this); Main.me.record_list.getWidget().removeWidget(this);
if (Main.me.is_recording && this.equals(Main.me.now_record)) {
Main.me.stopRecording();
Main.me.now_record = null;
return;
}
file.delete();
} }
} }

View file

@ -1,4 +1,4 @@
package themixray.repeating.mod; package ru.themixray.repeating_mod;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;

View file

@ -1,193 +1,187 @@
package themixray.repeating.mod; package ru.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 ru.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, 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);
} }
} }
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();
} }
};
@Override pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip")));
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta); import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> {
updateMessage(); new Thread(() -> {
} FileDialog fd = new FileDialog((java.awt.Frame) null);
}; fd.setMultipleMode(true);
pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip"))); fd.setName("Choose record files");
fd.setTitle("Choose record files");
import_btn = ButtonWidget.builder(Text.translatable("text.repeating-mod.import"), button -> { fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm"));
new Thread(() -> { fd.setVisible(true);
FileDialog fd = new FileDialog((java.awt.Frame) null);
fd.setMultipleMode(true); File[] files = fd.getFiles();
fd.setName("Choose record files"); if (files != null) {
fd.setTitle("Choose record files"); for (File file : files) {
fd.setFilenameFilter((dir, name) -> name.endsWith(".rrm")); try {
fd.setVisible(true); Main.me.setNowRecord(Main.me.record_list.cloneRecord(file));
} catch (Exception e) {
File[] files = fd.getFiles(); throw new RuntimeException(e);
if (files != null) { }
for (File file : files) { }
try { }}).start();
Main.me.record_list.addRecord(file); })
} catch (Exception e) { .dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20)
throw new RuntimeException(e); .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip")))
} .build();
}
}}).start(); was_build = true;
})
.dimensions(width / 2 + 2, height / 2 - list_widget.getHeight() / 2 - 22, 180, 20) updateButtons();
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip")))
.build(); addDrawableChild(loop_btn);
addDrawableChild(record_btn);
was_build = true; addDrawableChild(import_btn);
addDrawableChild(pos_delay_slider);
updateButtons(); }
addDrawableChild(loop_btn); public <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement) {
addDrawableChild(record_btn); return super.addDrawableChild(drawableElement);
addDrawableChild(import_btn); }
addDrawableChild(pos_delay_slider);
} public <T extends Drawable> T addDrawable(T drawable) {
return super.addDrawable(drawable);
public <T extends Element & Drawable & Selectable> T addDrawableChild(T drawableElement) { }
return super.addDrawableChild(drawableElement);
} public <T extends Element & Selectable> T addSelectableChild(T child) {
return super.addSelectableChild(child);
public <T extends Drawable> T addDrawable(T drawable) { }
return super.addDrawable(drawable);
} public void remove(Element child) {
super.remove(child);
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 +1,95 @@
package themixray.repeating.mod; package ru.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
}
public TickTask(long delay, TickAt at) {
this.is_cancelled = false; public TickTask(long delay, TickAt at) {
this.is_repeating = false; this.is_cancelled = false;
this.delay = delay; this.is_repeating = false;
this.living = 0; this.delay = delay;
this.period = 0; this.living = 0;
this.at = at; this.period = 0;
tasks.add(this); this.at = at;
} tasks.add(this);
}
public TickTask(long delay, long period, TickAt at) {
this.is_cancelled = false; public TickTask(long delay, long period, TickAt at) {
this.is_repeating = true; this.is_cancelled = false;
this.delay = delay; this.is_repeating = true;
this.period = period; this.delay = delay;
this.living = 0; this.period = period;
this.at = at; this.living = 0;
tasks.add(this); this.at = at;
} tasks.add(this);
}
public TickTask(long delay) {
this(delay,TickAt.CLIENT_HEAD); public TickTask(long delay) {
} this(delay,TickAt.CLIENT_HEAD);
}
public TickTask(long delay, long period) {
this(delay,period,TickAt.CLIENT_HEAD); public TickTask(long delay, long period) {
} this(delay,period,TickAt.CLIENT_HEAD);
}
public void cancel() {
if (!is_cancelled) { public void cancel() {
is_cancelled = true; if (!is_cancelled) {
tasks.remove(this); is_cancelled = true;
} tasks.remove(this);
} }
}
public boolean isCancelled() {
return is_cancelled; public boolean isCancelled() {
} return is_cancelled;
}
public TickAt getAt() {
return at; public TickAt getAt() {
} return at;
}
public void setDelay(long delay) {
if (is_repeating) { public void setDelay(long delay) {
this.delay = delay; if (is_repeating) {
} this.delay = delay;
} }
public long getDelay() { }
return this.delay; public long getDelay() {
} return this.delay;
}
public void tick() {
if (living >= delay) { public void tick() {
if (is_repeating) { if (living >= delay) {
delay = period; if (is_repeating) {
run(); delay = period;
living = -1; run();
} else { living = -1;
run(); } else {
cancel(); run();
} cancel();
} }
living++; }
} living++;
} }
}

View file

@ -0,0 +1,22 @@
package ru.themixray.repeating_mod.event;
import ru.themixray.repeating_mod.event.events.*;
public abstract class RecordEvent {
public abstract void replay();
public RecordEventType getType() {
for (RecordEventType ev : RecordEventType.values()) {
if (ev.getEventClass().getTypeName().equals(this.getClass().getTypeName())) {
return ev;
}
}
return null;
}
protected abstract String[] serializeArgs();
public String serialize() {
return getType().getChar() + "=" + String.join("&", serializeArgs());
}
public static RecordEvent deserialize(String t) {
return RecordEventType.getByChar(t.charAt(0)).deserialize(t.substring(2).split("&"));
}
}

View file

@ -0,0 +1,69 @@
package ru.themixray.repeating_mod.event;
import ru.themixray.repeating_mod.event.events.*;
public enum RecordEventType {
BLOCK_BREAK('b',"block_break",BlockBreakEvent.class),
BLOCK_INTERACT('i',"block_interact",BlockInteractEvent.class),
DELAY('d',"delay",DelayEvent.class),
INPUT('p',"input",InputEvent.class),
MOVE('m',"move",MoveEvent.class);
// GUI_KEY_PRESS('r',"key_press", GuiKeyPressEvent.class),
// GUI_KEY_RELEASE('s',"key_release",GuiKeyReleaseEvent.class),
// GUI_CHAR_TYPE('h',"char_type",GuiCharTypeEvent.class),
// GUI_MOUSE_CLICK('c',"mouse_click",GuiMouseClickEvent.class),
// GUI_MOUSE_RELEASE('l',"mouse_release",GuiMouseReleaseEvent.class),
// GUI_MOUSE_DRAG('g',"mouse_drag",GuiMouseDragEvent.class),
// GUI_MOUSE_MOVE('v',"mouse_move",GuiMouseMoveEvent.class),
// GUI_MOUSE_SCROLL('o',"mouse_scroll",GuiMouseScrollEvent.class),
// GUI_CLOSE('e',"close",GuiCloseEvent.class);
private Class<? extends RecordEvent> ev;
private char ch;
private String name;
RecordEventType(char ch, String name, Class<? extends RecordEvent> ev) {
this.ev = ev;
this.ch = ch;
this.name = name;
}
public Class<? extends RecordEvent> getEventClass() {
return ev;
}
public char getChar() {
return ch;
}
public String getName() {
return name;
}
public RecordEvent deserialize(String[] args) {
try {
return (RecordEvent) ev
.getMethod("deserialize", String[].class)
.invoke(null, (Object) args);
} catch (Throwable e) {
return null;
}
}
public String serialize(RecordEvent event) {
return event.serialize();
}
public static RecordEventType getByChar(String type) {
return getByChar(type.charAt(0));
}
public static RecordEventType getByChar(char ch) {
for (RecordEventType t : values()) {
if (t.getChar() == ch) {
return t;
}
}
return null;
}
}

View file

@ -0,0 +1,35 @@
package ru.themixray.repeating_mod.event.events;
import net.minecraft.util.math.BlockPos;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class BlockBreakEvent extends RecordEvent {
public BlockPos pos;
public BlockBreakEvent(
BlockPos pos) {
this.pos = pos;
}
public static BlockBreakEvent deserialize(String[] a) {
return new BlockBreakEvent(new BlockPos(
Integer.parseInt(a[0]),
Integer.parseInt(a[1]),
Integer.parseInt(a[2])));
}
protected String[] serializeArgs() {
return new String[]{
String.valueOf(pos.getX()),
String.valueOf(pos.getY()),
String.valueOf(pos.getZ())
};
}
public void replay() {
if (Main.client.interactionManager != null) {
Main.client.interactionManager.breakBlock(pos);
}
}
}

View file

@ -0,0 +1,51 @@
package ru.themixray.repeating_mod.event.events;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class BlockInteractEvent extends RecordEvent {
public Hand hand;
public BlockHitResult hitResult;
public static BlockInteractEvent deserialize(String[] a) {
return new BlockInteractEvent(
Hand.valueOf(a[5]),
new BlockHitResult(new Vec3d(
Double.parseDouble(a[0]),
Double.parseDouble(a[1]),
Double.parseDouble(a[2])),
Direction.byIndex(Integer.parseInt(a[4])),
new BlockPos(
Integer.parseInt(a[0]),
Integer.parseInt(a[1]),
Integer.parseInt(a[2])),
a[3].equals("1")));
}
public BlockInteractEvent(Hand hand, BlockHitResult hitResult) {
this.hand = hand;
this.hitResult = hitResult;
}
public void replay() {
if (Main.client.interactionManager != null) {
Main.client.interactionManager.interactBlock(Main.client.player, hand, hitResult);
}
}
protected String[] serializeArgs() {
return new String[]{
String.valueOf(hitResult.getBlockPos().getX()),
String.valueOf(hitResult.getBlockPos().getY()),
String.valueOf(hitResult.getBlockPos().getZ()),
(hitResult.isInsideBlock() ? "1" : "0"),
String.valueOf(hitResult.getSide().getIndex()),
hand.name()
};
}
}

View file

@ -0,0 +1,29 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.event.RecordEvent;
public class DelayEvent extends RecordEvent {
public long delay;
public static DelayEvent deserialize(String[] a) {
return new DelayEvent(Long.parseLong(a[0]));
}
public DelayEvent(long delay) {
this.delay = delay;
}
public void replay() {
try {
Thread.sleep(delay / 20 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected String[] serializeArgs() {
return new String[]{
String.valueOf(delay)
};
}
}

View file

@ -0,0 +1,34 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiCharTypeEvent extends RecordEvent {
private char chr;
private int modifiers;
public GuiCharTypeEvent(char chr, int modifiers) {
this.chr = chr;
this.modifiers = modifiers;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.charTyped(chr, modifiers);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf((int) chr),
String.valueOf(modifiers)
};
}
public static GuiCharTypeEvent deserialize(String[] args) {
return new GuiCharTypeEvent(
(char) Integer.parseInt(args[0]),
Integer.parseInt(args[1])
);
}
}

View file

@ -0,0 +1,22 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiCloseEvent extends RecordEvent {
public GuiCloseEvent() {}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.setScreen(null);
}
}
protected String[] serializeArgs() {
return new String[] {};
}
public static GuiCloseEvent deserialize(String[] args) {
return new GuiCloseEvent();
}
}

View file

@ -0,0 +1,38 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiKeyPressEvent extends RecordEvent {
private int keyCode;
private int scanCode;
private int modifiers;
public GuiKeyPressEvent(int keyCode, int scanCode, int modifiers) {
this.keyCode = keyCode;
this.scanCode = scanCode;
this.modifiers = modifiers;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.keyPressed(keyCode, scanCode, modifiers);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(keyCode),
String.valueOf(scanCode),
String.valueOf(modifiers)
};
}
public static GuiKeyPressEvent deserialize(String[] args) {
return new GuiKeyPressEvent(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2])
);
}
}

View file

@ -0,0 +1,38 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiKeyReleaseEvent extends RecordEvent {
private int keyCode;
private int scanCode;
private int modifiers;
public GuiKeyReleaseEvent(int keyCode, int scanCode, int modifiers) {
this.keyCode = keyCode;
this.scanCode = scanCode;
this.modifiers = modifiers;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.keyReleased(keyCode, scanCode, modifiers);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(keyCode),
String.valueOf(scanCode),
String.valueOf(modifiers)
};
}
public static GuiKeyReleaseEvent deserialize(String[] args) {
return new GuiKeyReleaseEvent(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2])
);
}
}

View file

@ -0,0 +1,38 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiMouseClickEvent extends RecordEvent {
private double mouseX;
private double mouseY;
private int button;
public GuiMouseClickEvent(double mouseX, double mouseY, int button) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.button = button;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.mouseClicked(mouseX, mouseY, button);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(mouseX),
String.valueOf(mouseY),
String.valueOf(button)
};
}
public static GuiMouseClickEvent deserialize(String[] args) {
return new GuiMouseClickEvent(
Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Integer.parseInt(args[2])
);
}
}

View file

@ -0,0 +1,46 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiMouseDragEvent extends RecordEvent {
private double mouseX;
private double mouseY;
private double deltaX;
private double deltaY;
private int button;
public GuiMouseDragEvent(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.button = button;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(mouseX),
String.valueOf(mouseY),
String.valueOf(button),
String.valueOf(deltaX),
String.valueOf(deltaY)
};
}
public static GuiMouseDragEvent deserialize(String[] args) {
return new GuiMouseDragEvent(
Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Integer.parseInt(args[2]),
Double.parseDouble(args[3]),
Double.parseDouble(args[4])
);
}
}

View file

@ -0,0 +1,34 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiMouseMoveEvent extends RecordEvent {
private double mouseX;
private double mouseY;
public GuiMouseMoveEvent(double mouseX, double mouseY) {
this.mouseX = mouseX;
this.mouseY = mouseY;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.mouseMoved(mouseX, mouseY);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(mouseX),
String.valueOf(mouseY)
};
}
public static GuiMouseMoveEvent deserialize(String[] args) {
return new GuiMouseMoveEvent(
Double.parseDouble(args[0]),
Double.parseDouble(args[1])
);
}
}

View file

@ -0,0 +1,38 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiMouseReleaseEvent extends RecordEvent {
private double mouseX;
private double mouseY;
private int button;
public GuiMouseReleaseEvent(double mouseX, double mouseY, int button) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.button = button;
}
public void replay() {
if (Main.client.currentScreen != null) {
Main.client.currentScreen.mouseReleased(mouseX, mouseY, button);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(mouseX),
String.valueOf(mouseY),
String.valueOf(button)
};
}
public static GuiMouseReleaseEvent deserialize(String[] args) {
return new GuiMouseReleaseEvent(
Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Integer.parseInt(args[2])
);
}
}

View file

@ -0,0 +1,38 @@
package ru.themixray.repeating_mod.event.events;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class GuiMouseScrollEvent extends RecordEvent {
private double mouseX;
private double mouseY;
private double amount;
public GuiMouseScrollEvent(double mouseX, double mouseY, double amount) {
this.mouseX = mouseX;
this.mouseY = mouseY;
this.amount = amount;
}
public void replay() {
if (Main.client.currentScreen != null) {
// Main.client.currentScreen.mouseScrolled(mouseX, mouseY, amount);
}
}
protected String[] serializeArgs() {
return new String[] {
String.valueOf(mouseX),
String.valueOf(mouseY),
String.valueOf(amount)
};
}
public static GuiMouseScrollEvent deserialize(String[] args) {
return new GuiMouseScrollEvent(
Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Double.parseDouble(args[2])
);
}
}

View file

@ -0,0 +1,195 @@
package ru.themixray.repeating_mod.event.events;
import net.minecraft.client.input.Input;
import net.minecraft.util.PlayerInput;
import net.minecraft.util.math.Vec2f;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
import java.lang.reflect.Field;
public class InputEvent extends RecordEvent {
public Boolean forward;
public Boolean backward;
public Boolean left;
public Boolean right;
public Boolean jump;
public Boolean sneak;
public Boolean sprint;
public float yaw;
public float head_yaw;
public float body_yaw;
public float pitch;
public float speed;
public float movementForward;
public float movementSideways;
public static InputEvent current() {
if (Main.client.player == null) return null;
return new InputEvent(
Main.client.player.input.playerInput.forward(),
Main.client.player.input.playerInput.backward(),
Main.client.player.input.playerInput.left(),
Main.client.player.input.playerInput.right(),
Main.client.player.input.playerInput.jump(),
Main.client.player.input.playerInput.sneak(),
Main.client.player.input.playerInput.sprint(),
Main.client.player.getHeadYaw(),
Main.client.player.getBodyYaw(),
Main.client.player.getPitch(),
Main.client.player.getYaw(),
Main.client.player.getMovementSpeed(),
Main.client.player.input.getMovementInput().y,
Main.client.player.input.getMovementInput().x
);
}
public InputEvent(Boolean forward,
Boolean backward,
Boolean left,
Boolean right,
Boolean jump,
Boolean sneak,
Boolean sprint,
float head_yaw,
float body_yaw,
float head_pitch,
float yaw,
float speed,
float movementForward,
float movementSideways) {
this.forward = forward;
this.backward = backward;
this.left = left;
this.right = right;
this.jump = jump;
this.sneak = sneak;
this.sprint = sprint;
this.head_yaw = head_yaw;
this.body_yaw = body_yaw;
this.pitch = head_pitch;
this.yaw = yaw;
this.speed = speed;
this.movementForward = movementForward;
this.movementSideways = movementSideways;
}
/**
* Returns differences of this InputEvent to the provided one, saving first booleans if differ and first floats always
*/
public InputEvent differs(InputEvent event) {
return new InputEvent(
forward == event.forward ? null : forward,
backward == event.backward ? null : backward,
left == event.left ? null : left,
right == event.right ? null : right,
jump == event.jump ? null : jump,
sneak == event.sneak ? null : sneak,
sprint == event.sprint ? null : sprint,
head_yaw,
body_yaw,
pitch,
yaw,
speed,
movementForward,
movementSideways
);
}
public static InputEvent deserialize(String[] a) {
return new InputEvent(
(a[0].equals("n") ? null : a[0].equals("1")),
(a[1].equals("n") ? null : a[1].equals("1")),
(a[2].equals("n") ? null : a[2].equals("1")),
(a[3].equals("n") ? null : a[3].equals("1")),
(a[4].equals("n") ? null : a[4].equals("1")),
(a[5].equals("n") ? null : a[5].equals("1")),
(a[6].equals("n") ? null : a[6].equals("1")),
Float.parseFloat(a[7]),
Float.parseFloat(a[8]),
Float.parseFloat(a[9]),
Float.parseFloat(a[10]),
Float.parseFloat(a[11]),
Float.parseFloat(a[12]),
Float.parseFloat(a[13])
);
}
protected String[] serializeArgs() {
return new String[] {
((forward == null) ? "n" : (forward ? "1" : "0")),
((backward == null) ? "n" : (backward ? "1" : "0")),
((left == null) ? "n" : (left ? "1" : "0")),
((right == null) ? "n" : (right ? "1" : "0")),
((jump == null) ? "n" : (jump ? "1" : "0")),
((sneak == null) ? "n" : (sneak ? "1" : "0")),
((sprint == null) ? "n" : (sprint ? "1" : "0")),
String.valueOf(head_yaw),
String.valueOf(body_yaw),
String.valueOf(pitch),
String.valueOf(yaw),
String.valueOf(speed),
String.valueOf(movementForward),
String.valueOf(movementSideways)
};
}
public boolean equals(InputEvent event) {
return event.forward == forward &&
event.backward == backward &&
event.sprint == sprint &&
event.jump == jump &&
event.sneak == sneak &&
event.left == left &&
event.right == right &&
event.speed == speed &&
event.head_yaw == head_yaw &&
event.body_yaw == body_yaw &&
event.yaw == yaw &&
event.pitch == pitch &&
event.movementForward == movementForward &&
event.movementSideways == movementSideways;
}
public void replay() {
Main.input_replay = this;
}
public void inputCallback() {
if (Main.client.player != null) {
if (sprint != null && Main.client.player.isSprinting() != sprint)
Main.client.player.setSprinting(sprint);
if (Main.client.player.getYaw() != yaw)
Main.client.player.setYaw(yaw);
if (Main.client.player.getHeadYaw() != head_yaw)
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);
try {
Field field = Input.class.getDeclaredField("movementVector");
field.setAccessible(true);
field.set(Main.client.player.input, new Vec2f(movementSideways, movementForward));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
PlayerInput input = Main.client.player.input.playerInput;
Main.client.player.input.playerInput = new PlayerInput(
this.forward == null ? input.forward() : this.forward,
this.backward == null ? input.backward() : this.backward,
this.left == null ? input.left() : this.left,
this.right == null ? input.right() : this.right,
this.jump == null ? input.jump() : this.jump,
this.sneak == null ? input.sneak() : this.sneak,
this.sprint == null ? input.sprint() : this.sprint
);
}
}
}

View file

@ -0,0 +1,52 @@
package ru.themixray.repeating_mod.event.events;
import net.minecraft.entity.MovementType;
import net.minecraft.util.math.Vec3d;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.RecordEvent;
public class MoveEvent extends RecordEvent {
public Vec3d vec;
public float yaw;
public float pitch;
public static final float MOVE_THRESHOLD = 0.001f;
public static MoveEvent deserialize(String[] a) {
return new MoveEvent(new Vec3d(
Double.parseDouble(a[0]),
Double.parseDouble(a[1]),
Double.parseDouble(a[2])),
Float.parseFloat(a[3]),
Float.parseFloat(a[4]));
}
public MoveEvent(Vec3d vec, float yaw, float pitch) {
this.vec = vec;
this.yaw = yaw;
this.pitch = pitch;
}
public void replay() {
if (Main.client.player != null) {
Vec3d p = Main.client.player.getPos();
Vec3d v = new Vec3d(vec.getX() - p.getX(), vec.getY() - p.getY(), vec.getZ() - p.getZ());
if (Math.abs(v.x) > MOVE_THRESHOLD || Math.abs(v.y) > MOVE_THRESHOLD || Math.abs(v.z) > MOVE_THRESHOLD)
Main.client.player.move(MovementType.SELF, v);
if (Math.abs(Main.client.player.getYaw() - yaw) > MOVE_THRESHOLD)
Main.client.player.setYaw(yaw);
if (Math.abs(Main.client.player.getPitch() - pitch) > MOVE_THRESHOLD)
Main.client.player.setPitch(pitch);
}
}
protected String[] serializeArgs() {
return new String[]{
String.valueOf(vec.getX()),
String.valueOf(vec.getZ()),
String.valueOf(yaw),
String.valueOf(pitch)
};
}
}

View file

@ -1,24 +1,24 @@
package themixray.repeating.mod.mixin; package ru.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 ru.themixray.repeating_mod.Main;
import themixray.repeating.mod.TickTask; import ru.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,30 +1,31 @@
package themixray.repeating.mod.mixin; package ru.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 ru.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 (getUuid().equals(Main.client.player.getUuid())) { if (Main.client.player != null) {
if (Main.me.is_replaying) { if (getUuid().equals(Main.client.player.getUuid())) {
if (Main.input_replay != null && if (Main.me.is_replaying) {
Main.input_replay.sprinting != null && if (Main.input_replay != null &&
Main.input_replay.sprinting != sprinting) { Main.input_replay.sprint != null &&
ci.cancel(); Main.input_replay.sprint != sprinting) {
return; ci.cancel();
} }
} }
} }
} }
} }
}

View file

@ -1,20 +1,20 @@
package themixray.repeating.mod.mixin; package ru.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 ru.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(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 ru.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 ru.themixray.repeating_mod.Main;
import themixray.repeating.mod.event.RecordBlockBreakEvent; import ru.themixray.repeating_mod.event.events.BlockBreakEvent;
import themixray.repeating.mod.event.RecordBlockInteractEvent; import ru.themixray.repeating_mod.event.events.BlockInteractEvent;
import themixray.repeating.mod.TickTask; import ru.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 RecordBlockBreakEvent(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 RecordBlockInteractEvent(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

@ -0,0 +1,29 @@
package ru.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

@ -0,0 +1,19 @@
package ru.themixray.repeating_mod.mixin;
import net.minecraft.network.ClientConnection;
import net.minecraft.text.Text;
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 ru.themixray.repeating_mod.Main;
@Mixin(ClientConnection.class)
public abstract class PlayerMixin {
@Inject(at = @At(value = "HEAD"), method = "disconnect")
private void disconnect(Text disconnectReason, CallbackInfo ci) {
if (Main.me.is_replaying) {
Main.me.stopReplay();
}
}
}

View file

@ -1,21 +1,21 @@
package themixray.repeating.mod.mixin; package ru.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 ru.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

@ -0,0 +1,85 @@
package ru.themixray.repeating_mod.mixin;
import net.minecraft.client.gui.AbstractParentElement;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.screen.Screen;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import ru.themixray.repeating_mod.Main;
import ru.themixray.repeating_mod.event.events.*;
@Mixin(Screen.class)
public abstract class ScreenMixin extends AbstractParentElement implements Drawable {
@Inject(at = @At(value = "HEAD"), method = "close")
private void close(CallbackInfo ci) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiCloseEvent());
}
}
@Inject(at = @At(value = "HEAD"), method = "keyPressed")
private void keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiKeyPressEvent(keyCode, scanCode, modifiers));
}
}
@Override
public boolean charTyped(char chr, int modifiers) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiCharTypeEvent(chr, modifiers));
}
return super.charTyped(chr, modifiers);
}
@Override
public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiKeyReleaseEvent(keyCode, scanCode, modifiers));
}
return super.keyReleased(keyCode, scanCode, modifiers);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiMouseClickEvent(mouseX, mouseY, button));
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public void mouseMoved(double mouseX, double mouseY) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiMouseMoveEvent(mouseX, mouseY));
}
super.mouseMoved(mouseX, mouseY);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiMouseDragEvent(mouseX, mouseY, button, deltaX, deltaY));
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (Main.me.is_recording) {
// Main.me.now_record.addEvent(new GuiMouseReleaseEvent(mouseX, mouseY, button));
}
return super.mouseReleased(mouseX, mouseY, button);
}
// @Override
// public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
// if (Main.me.is_recording) {
//// Main.me.now_record.addEvent(new GuiMouseScrollEvent(mouseX, mouseY, amount));
// }
// return super.mouseScrolled(mouseX, mouseY, amount);
// }
}

View file

@ -1,10 +1,10 @@
package themixray.repeating.mod.render; package ru.themixray.repeating_mod.render;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.util.math.Vec3d; import org.joml.Vector3f;
import themixray.repeating.mod.render.buffer.WorldBuffer; import ru.themixray.repeating_mod.render.buffer.WorldBuffer;
import themixray.repeating.mod.render.shader.ShaderManager; import ru.themixray.repeating_mod.render.shader.ShaderManager;
import java.awt.*; import java.awt.*;
@ -27,8 +27,8 @@ public class RenderHelper {
} }
public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) { 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(new Vector3f(x1, y1, z1), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); buffer.vert(new Vector3f(x2, y2, z2), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
} }
public static WorldBuffer startTri(WorldRenderContext context) { public static WorldBuffer startTri(WorldRenderContext context) {
@ -49,9 +49,9 @@ public class RenderHelper {
} }
public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) { 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(new Vector3f(x1, y1, z1), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); buffer.vert(new Vector3f(x2, y2, z2), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f); buffer.vert(new Vector3f(x3, y3, z3), color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
} }
public static void drawRectFromTri(WorldBuffer buffer, public static void drawRectFromTri(WorldBuffer buffer,

View file

@ -1,8 +1,8 @@
package themixray.repeating.mod.render; package ru.themixray.repeating_mod.render;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import themixray.repeating.mod.render.buffer.BufferManager; import ru.themixray.repeating_mod.render.buffer.BufferManager;
import themixray.repeating.mod.render.shader.ShaderManager; import ru.themixray.repeating_mod.render.shader.ShaderManager;
@UtilityClass @UtilityClass
public class RenderSystem { public class RenderSystem {

View file

@ -1,4 +1,4 @@
package themixray.repeating.mod.render.buffer; package ru.themixray.repeating_mod.render.buffer;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;

View file

@ -1,14 +1,11 @@
package themixray.repeating.mod.render.buffer; package ru.themixray.repeating_mod.render.buffer;
import lombok.Getter; import lombok.Getter;
import org.joml.Vector3f;
public class Vertex { public class Vertex {
@Getter @Getter
private float x; private Vector3f pos;
@Getter
private float y;
@Getter
private float z;
@Getter @Getter
private float r; private float r;
@Getter @Getter
@ -18,10 +15,8 @@ public class Vertex {
@Getter @Getter
private float a; private float a;
public Vertex(float x, float y, float z, float r, float g, float b, float a) { public Vertex(Vector3f pos, float r, float g, float b, float a) {
this.x = x; this.pos = pos;
this.y = y;
this.z = z;
this.r = r; this.r = r;
this.g = g; this.g = g;
this.b = b; this.b = b;

View file

@ -1,11 +1,12 @@
package themixray.repeating.mod.render.buffer; package ru.themixray.repeating_mod.render.buffer;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.util.math.Vec3d;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import themixray.repeating.mod.render.shader.Shader; import ru.themixray.repeating_mod.render.shader.Shader;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -18,17 +19,19 @@ public class WorldBuffer {
private final int drawMode; private final int drawMode;
private final Shader shader; private final Shader shader;
private FloatBuffer projectionMatrix; private FloatBuffer projectionMatrix;
private final Vec3d cameraPos; private final Vector3f cameraPos;
private final Quaternionf cameraRot;
public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) { public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) {
this.drawMode = drawMode; this.drawMode = drawMode;
this.shader = shader; this.shader = shader;
this.cameraPos = worldRenderContext.camera().getPos(); this.cameraPos = worldRenderContext.camera().getPos().toVector3f();
makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix()); this.cameraRot = worldRenderContext.camera().getRotation().invert();
this.projectionMatrix = worldRenderContext.projectionMatrix().mul(worldRenderContext.matrixStack().peek().getPositionMatrix()).get(BufferUtils.createFloatBuffer(16));
} }
public void vert(float x, float y, float z, float r, float g, float b, float a) { public void vert(Vector3f pos, 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)); vertices.add(new Vertex(cameraRot.transform(pos.sub(cameraPos)), r, g, b, a));
} }
public void draw() { public void draw() {
@ -36,7 +39,7 @@ public class WorldBuffer {
BufferManager.bindBuffer(); BufferManager.bindBuffer();
BufferManager.writeBuffer(getBuffer()); BufferManager.writeBuffer(getBuffer());
applyProjectionMatrix(); shader.uniformMatrix4f("u_projection", projectionMatrix);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
@ -55,12 +58,12 @@ public class WorldBuffer {
private FloatBuffer getBuffer() { private FloatBuffer getBuffer() {
FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7); FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7);
ArrayList<Float> floats = new ArrayList<>(); ArrayList<Float> floats = new ArrayList<>();
for (Vertex vertex : vertices) { for (ru.themixray.repeating_mod.render.buffer.Vertex vertex : vertices) {
floats.add(vertex.getX()); floats.add(vertex.getPos().x);
floats.add(vertex.getY()); floats.add(vertex.getPos().y);
floats.add(vertex.getZ()); floats.add(vertex.getPos().z);
} }
for (Vertex vertex : vertices) { for (ru.themixray.repeating_mod.render.buffer.Vertex vertex : vertices) {
floats.add(vertex.getR()); floats.add(vertex.getR());
floats.add(vertex.getG()); floats.add(vertex.getG());
floats.add(vertex.getB()); floats.add(vertex.getB());
@ -71,12 +74,4 @@ public class WorldBuffer {
floatBuffer.put(ArrayUtils.toPrimitive(floatArray)); floatBuffer.put(ArrayUtils.toPrimitive(floatArray));
return floatBuffer.flip(); 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,4 +1,4 @@
package themixray.repeating.mod.render.shader; package ru.themixray.repeating_mod.render.shader;
import lombok.Getter; import lombok.Getter;
@ -12,8 +12,8 @@ public class Shader {
public Shader(String name) { public Shader(String name) {
int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX); int v = ru.themixray.repeating_mod.render.shader.ShaderManager.loadShaderProgram(name, ru.themixray.repeating_mod.render.shader.ShaderManager.ShaderType.VERTEX);
int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT); int f = ru.themixray.repeating_mod.render.shader.ShaderManager.loadShaderProgram(name, ru.themixray.repeating_mod.render.shader.ShaderManager.ShaderType.FRAGMENT);
this.id = glCreateProgram(); this.id = glCreateProgram();
glAttachShader(id, v); glAttachShader(id, v);
glAttachShader(id, f); glAttachShader(id, f);

View file

@ -1,6 +1,6 @@
package themixray.repeating.mod.render.shader; package ru.themixray.repeating_mod.render.shader;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.opengl.GlStateManager;
import com.mojang.blaze3d.platform.TextureUtil; import com.mojang.blaze3d.platform.TextureUtil;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -27,21 +27,21 @@ import static org.lwjgl.opengl.GL33.*;
@UtilityClass @UtilityClass
public class ShaderManager { public class ShaderManager {
@Getter @Getter
private Shader positionColorShader; private ru.themixray.repeating_mod.render.shader.Shader positionColorShader;
public void init() { public void init() {
ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders()); ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders());
} }
private void loadShaders() { private void loadShaders() {
positionColorShader = new Shader("position_color"); positionColorShader = new ru.themixray.repeating_mod.render.shader.Shader("position_color");
} }
public int loadShaderProgram(String name, ShaderType type) { public int loadShaderProgram(String name, ShaderType type) {
try { try {
boolean file_present = true; boolean file_present = true;
ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager(); ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager();
Optional<Resource> resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension)); Optional<Resource> resource = resourceFactory.getResource(Identifier.of("renderer", "shader/" + name + type.fileExtension));
int i = glCreateShader(type.glType); int i = glCreateShader(type.glType);
if (resource.isPresent()) { if (resource.isPresent()) {
GlStateManager.glShaderSource(i, new GlImportProcessor() { GlStateManager.glShaderSource(i, new GlImportProcessor() {
@ -51,7 +51,7 @@ public class ShaderManager {
public String loadImport(boolean inline, String name) { public String loadImport(boolean inline, String name) {
return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8); return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8);
} }
}.readSource(readResourceAsString(resource.get().getInputStream()))); }.readSource(readResourceAsString(resource.get().getInputStream())).getFirst());
} else file_present = false; } else file_present = false;
glCompileShader(i); glCompileShader(i);
if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) { if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) {

View file

@ -1,24 +1,17 @@
package themixray.repeating.mod.widget; package ru.themixray.repeating_mod.widget;
import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; 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.gui.widget.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import themixray.repeating.mod.Main; import ru.themixray.repeating_mod.Main;
import themixray.repeating.mod.RecordState; import ru.themixray.repeating_mod.RecordState;
import themixray.repeating.mod.RepeatingScreen; import ru.themixray.repeating_mod.RepeatingScreen;
import java.util.ArrayList; import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
public class RecordListWidget extends ScrollableWidget { public class RecordListWidget extends ScrollableWidget {
private List<RecordWidget> widgets = new ArrayList<>(); private LinkedList<RecordWidget> widgets = new LinkedList<>();
private boolean focused = false; private boolean focused = false;
public RecordListWidget(int x, int y, int width, int height) { public RecordListWidget(int x, int y, int width, int height) {
@ -33,27 +26,40 @@ public class RecordListWidget extends ScrollableWidget {
return res; return res;
} }
@Override
protected int getContentsHeightWithPadding() {
return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0;
}
@Override @Override
protected double getDeltaYPerScroll() { protected double getDeltaYPerScroll() {
return 10; return 10;
} }
@Override @Override
protected void renderContents(DrawContext ctx, int mouseX, int mouseY, float delta) { protected void renderWidget(DrawContext ctx, int mouseX, int mouseY, float delta) {
int y = 0; ctx.fill(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight(), 0xff000000);
ctx.enableScissor(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight());
int y = (int) -this.getScrollY();
for (RecordWidget wid: widgets) { for (RecordWidget wid: widgets) {
wid.setY(y); wid.setY(y);
wid.render(ctx, mouseX, (int) (mouseY + this.getScrollY()), delta); wid.render(ctx, mouseX, (int) (mouseY), delta);
y += wid.getHeight(); y += wid.getHeight();
y += 2; y += 2;
} }
ctx.disableScissor();
this.drawScrollbar(ctx);
} }
public void addWidget(RecordState record) { public void addWidget(RecordState record) {
RecordWidget widget = new RecordWidget(0, 0, width, 55, record, this); RecordWidget widget = new RecordWidget(0, 0, width - 6, 55, record, this);
widget.init(null); widget.init(null);
widgets.add(0, widget); widgets.addFirst(widget);
} }
public void removeWidget(RecordState record) { public void removeWidget(RecordState record) {
@ -70,11 +76,6 @@ public class RecordListWidget extends ScrollableWidget {
return focused; return focused;
} }
@Override
protected int getContentsHeight() {
return !widgets.isEmpty() ? widgets.size() * 55 + (widgets.size() - 1) * 2 : 0;
}
public void init(RepeatingScreen screen) { public void init(RepeatingScreen screen) {
for (RecordWidget widget : widgets) { for (RecordWidget widget : widgets) {
widget.init(screen); widget.init(screen);
@ -104,7 +105,7 @@ public class RecordListWidget extends ScrollableWidget {
public boolean checkTransport(transport tr) { public boolean checkTransport(transport tr) {
for (RecordWidget wid : widgets) { for (RecordWidget wid : widgets) {
for (ClickableWidget child : wid.getChildren()) { for (ClickableWidget child : wid.getChildren()) {
if (tr.check(child)) { if (child.isFocused() && tr.check(child)) {
return true; return true;
} }
} }
@ -112,12 +113,14 @@ public class RecordListWidget extends ScrollableWidget {
return false; return false;
} }
public boolean checkTransportNF(transport tr) { public boolean checkTransportNF(double mouseX, double mouseY, int button) {
for (RecordWidget wid : widgets) { for (RecordWidget wid : widgets) {
for (ClickableWidget child : wid.getChildren()) { if (wid.contains((int) mouseX, (int) mouseY)) {
boolean res = tr.check(child); Main.me.setNowRecord(wid.getRecord());
}
if (res) { for (ClickableWidget child : wid.getChildren()) {
if (child.mouseClicked(mouseX, mouseY, button)) {
child.setFocused(true); child.setFocused(true);
return true; return true;
} else { } else {
@ -130,7 +133,7 @@ public class RecordListWidget extends ScrollableWidget {
@Override @Override
public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseClicked(double mouseX, double mouseY, int button) {
return checkTransportNF((c) -> c.mouseClicked(mouseX, mouseY + this.getScrollY(), button)) || super.mouseClicked(mouseX, mouseY, button); return checkTransportNF(mouseX, mouseY, button) || super.checkScrollbarDragged(mouseX, mouseY, button);
} }
@Override @Override

View file

@ -1,15 +1,15 @@
package themixray.repeating.mod.widget; package ru.themixray.repeating_mod.widget;
import lombok.Getter;
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.tooltip.Tooltip; import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.*; import net.minecraft.client.gui.widget.*;
import net.minecraft.text.Style;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import themixray.repeating.mod.Main; import net.minecraft.util.Formatting;
import themixray.repeating.mod.RecordState; import ru.themixray.repeating_mod.Main;
import themixray.repeating.mod.RenderListener; import ru.themixray.repeating_mod.RecordState;
import themixray.repeating.mod.RepeatingScreen; import ru.themixray.repeating_mod.RepeatingScreen;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
@ -19,6 +19,7 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
public class RecordWidget implements Drawable, Widget { public class RecordWidget implements Drawable, Widget {
@Getter
private RecordState record; private RecordState record;
private List<ClickableWidget> children; private List<ClickableWidget> children;
@ -42,6 +43,13 @@ public class RecordWidget implements Drawable, Widget {
this.children = new ArrayList<>(); this.children = new ArrayList<>();
} }
public boolean contains(int x, int y) {
return parent.getX() + getX() <= x &&
parent.getY() + getY() <= y &&
x <= parent.getX() + getX() + getWidth() &&
y <= parent.getY() + getY() + getHeight();
}
public void setX(int x) { public void setX(int x) {
this.x = x; this.x = x;
} }
@ -101,6 +109,11 @@ public class RecordWidget implements Drawable, Widget {
children.add(delete_button); children.add(delete_button);
ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> { ButtonWidget export_button = ButtonWidget.builder(Text.translatable("text.repeating-mod.export"), (i) -> {
try {
record.save();
} catch (IOException e) {
throw new RuntimeException(e);
}
if (Desktop.isDesktopSupported()) { if (Desktop.isDesktopSupported()) {
Desktop desk = Desktop.getDesktop(); Desktop desk = Desktop.getDesktop();
try { try {
@ -112,41 +125,40 @@ public class RecordWidget implements Drawable, Widget {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
} else {
Main.sendMessage(Text.literal("Record file is ").append(
Text.literal("["+record.getFile().getAbsolutePath()+"]").styled((s) -> s.withColor(Formatting.GRAY))));
} }
}).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build(); }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 14, 65, 13).build();
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)) {
return;
}
} }
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);
}).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13) }).dimensions(parent.getX() + getX() + 110,parent.getY() + getY() + 4 + 28, 65, 13)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build(); .tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip"))).build();
children.add(replay_button); children.add(replay_button);
} }
public RecordState getRecord() { public void drawText(int x, int y, DrawContext ctx, List<Text> lines, int line_height, boolean shadow) {
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; int now_y = y;
for (Text line : lines) { for (Text line : lines) {
ctx.drawText(Main.client.textRenderer, line, (int) (x / size), (int) (now_y / size), line.getStyle().getColor().getRgb(), shadow); ctx.drawText(Main.client.textRenderer, line, x, now_y, 0xff000000 + line.getStyle().getColor().getRgb(), shadow);
now_y += line_height; now_y += line_height;
} }
ctx.getMatrices().pop();
} }
@Override @Override
@ -171,7 +183,7 @@ public class RecordWidget implements Drawable, Widget {
.append(": ") .append(": ")
.styled((s) -> s.withColor(0xbbbbbb)), .styled((s) -> s.withColor(0xbbbbbb)),
Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff)) Text.literal(record.getAuthor()).styled((s) -> s.withColor(0xffffff))
), 1, ),
9, 9,
false); false);

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,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,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": "WTFPL",
"icon": "icon.png", "icon": "icon.png",
"environment": "client", "environment": "client",
"entrypoints": { "entrypoints": {
"client": [ "client": [
"themixray.repeating.mod.Main" "ru.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.21.4",
"java": ">=17" "java": ">=17"
}, },
"suggests": { "suggests": {
"another-mod": "*" "another-mod": "*"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

@ -1,18 +1,20 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "themixray.repeating.mod.mixin", "package": "ru.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",
"injectors": { "PlayerMixin"
"defaultRequire": 1 ],
} "injectors": {
} "defaultRequire": 1
}
}