commit
cd42d8e5b1
121
LICENSE
121
LICENSE
@ -1,121 +0,0 @@
|
|||||||
Creative Commons Legal Code
|
|
||||||
|
|
||||||
CC0 1.0 Universal
|
|
||||||
|
|
||||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
|
||||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
|
||||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
|
||||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
|
||||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
|
||||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
|
||||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
|
||||||
HEREUNDER.
|
|
||||||
|
|
||||||
Statement of Purpose
|
|
||||||
|
|
||||||
The laws of most jurisdictions throughout the world automatically confer
|
|
||||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
|
||||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
|
||||||
authorship and/or a database (each, a "Work").
|
|
||||||
|
|
||||||
Certain owners wish to permanently relinquish those rights to a Work for
|
|
||||||
the purpose of contributing to a commons of creative, cultural and
|
|
||||||
scientific works ("Commons") that the public can reliably and without fear
|
|
||||||
of later claims of infringement build upon, modify, incorporate in other
|
|
||||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
|
||||||
and for any purposes, including without limitation commercial purposes.
|
|
||||||
These owners may contribute to the Commons to promote the ideal of a free
|
|
||||||
culture and the further production of creative, cultural and scientific
|
|
||||||
works, or to gain reputation or greater distribution for their Work in
|
|
||||||
part through the use and efforts of others.
|
|
||||||
|
|
||||||
For these and/or other purposes and motivations, and without any
|
|
||||||
expectation of additional consideration or compensation, the person
|
|
||||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
|
||||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
|
||||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
|
||||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
|
||||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
|
||||||
|
|
||||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
|
||||||
protected by copyright and related or neighboring rights ("Copyright and
|
|
||||||
Related Rights"). Copyright and Related Rights include, but are not
|
|
||||||
limited to, the following:
|
|
||||||
|
|
||||||
i. the right to reproduce, adapt, distribute, perform, display,
|
|
||||||
communicate, and translate a Work;
|
|
||||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
|
||||||
iii. publicity and privacy rights pertaining to a person's image or
|
|
||||||
likeness depicted in a Work;
|
|
||||||
iv. rights protecting against unfair competition in regards to a Work,
|
|
||||||
subject to the limitations in paragraph 4(a), below;
|
|
||||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
|
||||||
in a Work;
|
|
||||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
|
||||||
European Parliament and of the Council of 11 March 1996 on the legal
|
|
||||||
protection of databases, and under any national implementation
|
|
||||||
thereof, including any amended or successor version of such
|
|
||||||
directive); and
|
|
||||||
vii. other similar, equivalent or corresponding rights throughout the
|
|
||||||
world based on applicable law or treaty, and any national
|
|
||||||
implementations thereof.
|
|
||||||
|
|
||||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
|
||||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
|
||||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
|
||||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
|
||||||
of action, whether now known or unknown (including existing as well as
|
|
||||||
future claims and causes of action), in the Work (i) in all territories
|
|
||||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
|
||||||
treaty (including future time extensions), (iii) in any current or future
|
|
||||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
|
||||||
including without limitation commercial, advertising or promotional
|
|
||||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
|
||||||
member of the public at large and to the detriment of Affirmer's heirs and
|
|
||||||
successors, fully intending that such Waiver shall not be subject to
|
|
||||||
revocation, rescission, cancellation, termination, or any other legal or
|
|
||||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
|
||||||
as contemplated by Affirmer's express Statement of Purpose.
|
|
||||||
|
|
||||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
|
||||||
be judged legally invalid or ineffective under applicable law, then the
|
|
||||||
Waiver shall be preserved to the maximum extent permitted taking into
|
|
||||||
account Affirmer's express Statement of Purpose. In addition, to the
|
|
||||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
|
||||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
|
||||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
|
||||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
|
||||||
maximum duration provided by applicable law or treaty (including future
|
|
||||||
time extensions), (iii) in any current or future medium and for any number
|
|
||||||
of copies, and (iv) for any purpose whatsoever, including without
|
|
||||||
limitation commercial, advertising or promotional purposes (the
|
|
||||||
"License"). The License shall be deemed effective as of the date CC0 was
|
|
||||||
applied by Affirmer to the Work. Should any part of the License for any
|
|
||||||
reason be judged legally invalid or ineffective under applicable law, such
|
|
||||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
|
||||||
of the License, and in such case Affirmer hereby affirms that he or she
|
|
||||||
will not (i) exercise any of his or her remaining Copyright and Related
|
|
||||||
Rights in the Work or (ii) assert any associated claims and causes of
|
|
||||||
action with respect to the Work, in either case contrary to Affirmer's
|
|
||||||
express Statement of Purpose.
|
|
||||||
|
|
||||||
4. Limitations and Disclaimers.
|
|
||||||
|
|
||||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
|
||||||
surrendered, licensed or otherwise affected by this document.
|
|
||||||
b. Affirmer offers the Work as-is and makes no representations or
|
|
||||||
warranties of any kind concerning the Work, express, implied,
|
|
||||||
statutory or otherwise, including without limitation warranties of
|
|
||||||
title, merchantability, fitness for a particular purpose, non
|
|
||||||
infringement, or the absence of latent or other defects, accuracy, or
|
|
||||||
the present or absence of errors, whether or not discoverable, all to
|
|
||||||
the greatest extent permissible under applicable law.
|
|
||||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
|
||||||
that may apply to the Work or any use thereof, including without
|
|
||||||
limitation any person's Copyright and Related Rights in the Work.
|
|
||||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
|
||||||
consents, permissions or other rights required for any use of the
|
|
||||||
Work.
|
|
||||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
|
||||||
party to this document and has no duty or obligation with respect to
|
|
||||||
this CC0 or use of the Work.
|
|
18
README.md
18
README.md
@ -1,9 +1,17 @@
|
|||||||
# Fabric Example Mod
|
# Repeating Mod (Fabric)
|
||||||
|
|
||||||
## Setup
|
This mod can record your movements and play them back.
|
||||||
|
|
||||||
For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using.
|

|
||||||
|
|
||||||
## License
|
## Controls
|
||||||
|
|
||||||
This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects.
|
```
|
||||||
|
Menu | J
|
||||||
|
Toggle recording | not specified
|
||||||
|
Toggle replay | not specified
|
||||||
|
```
|
||||||
|
|
||||||
|
## Menu
|
||||||
|
|
||||||
|

|
||||||
|
18
build.gradle
18
build.gradle
@ -13,9 +13,17 @@ repositories {
|
|||||||
// 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' }
|
maven { url 'https://maven.wispforest.io' }
|
||||||
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compileOnly 'org.projectlombok:lombok:1.18.24'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok:1.18.24'
|
||||||
|
|
||||||
|
//add joml
|
||||||
|
modImplementation 'org.joml:joml:1.10.4'
|
||||||
|
include 'org.joml:joml:1.10.4'
|
||||||
|
|
||||||
// To change the versions see the gradle.properties file
|
// To change the versions see the gradle.properties file
|
||||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||||
@ -29,15 +37,13 @@ dependencies {
|
|||||||
|
|
||||||
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
|
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
|
||||||
|
|
||||||
modImplementation "io.wispforest:owo-lib:${project.owo_version}"
|
// modImplementation "io.wispforest:owo-lib:${project.owo_version}"
|
||||||
// only if you plan to use owo-config
|
// // only if you plan to use owo-config
|
||||||
annotationProcessor "io.wispforest:owo-lib:${project.owo_version}"
|
// annotationProcessor "io.wispforest:owo-lib:${project.owo_version}"
|
||||||
|
|
||||||
// include this if you don't want force your users to install owo
|
// 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
|
// sentinel will warn them and give the option to download it automatically
|
||||||
include "io.wispforest:owo-sentinel:${project.owo_version}"
|
// include "io.wispforest:owo-sentinel:${project.owo_version}"
|
||||||
// https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple
|
|
||||||
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base {
|
base {
|
||||||
|
@ -4,16 +4,16 @@ 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.19.3
|
minecraft_version=1.20
|
||||||
yarn_mappings=1.19.3+build.1
|
yarn_mappings=1.20+build.1
|
||||||
loader_version=0.14.17
|
loader_version=0.14.23
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 1.0.0
|
mod_version = 1.0.7
|
||||||
maven_group = themixray.repeating.mod
|
maven_group = themixray.repeating.mod
|
||||||
archives_base_name = repeating-mod
|
archives_base_name = repeating-mod
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
fabric_version=0.76.1+1.19.3
|
fabric_version=0.83.0+1.20
|
||||||
|
|
||||||
owo_version=0.10.3+1.19.3
|
#owo_version=0.11.1+1.20
|
||||||
|
BIN
preview.gif
Normal file
BIN
preview.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 MiB |
103
src/main/java/themixray/repeating/mod/EasyConfig.java
Normal file
103
src/main/java/themixray/repeating/mod/EasyConfig.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package themixray.repeating.mod;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class EasyConfig {
|
||||||
|
public final Path path;
|
||||||
|
public final File file;
|
||||||
|
public Map<String,String> data;
|
||||||
|
|
||||||
|
public EasyConfig(File f, Map<String,String> def) {
|
||||||
|
this.path = f.toPath();
|
||||||
|
this.file = f;
|
||||||
|
this.data = new HashMap<>();
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
try {
|
||||||
|
file.createNewFile();
|
||||||
|
write(def);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reload();
|
||||||
|
|
||||||
|
for (Map.Entry<String,String> m:def.entrySet())
|
||||||
|
if (!data.containsKey(m.getKey()))
|
||||||
|
data.put(m.getKey(),m.getValue());
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
public EasyConfig(Path f, Map<String,String> def) {
|
||||||
|
this(f.toFile(),def);
|
||||||
|
}
|
||||||
|
public EasyConfig(String parent,String child,Map<String,String> def) {
|
||||||
|
this(new File(parent,child),def);
|
||||||
|
}
|
||||||
|
public EasyConfig(File parent,String child,Map<String,String> def) {
|
||||||
|
this(new File(parent,child),def);
|
||||||
|
}
|
||||||
|
public EasyConfig(Path parent,String child,Map<String,String> def) {
|
||||||
|
this(new File(parent.toFile(),child),def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EasyConfig(File f) {
|
||||||
|
this(f,new HashMap<>());
|
||||||
|
}
|
||||||
|
public EasyConfig(Path path) {
|
||||||
|
this(path.toFile(),new HashMap<>());
|
||||||
|
}
|
||||||
|
public EasyConfig(String parent,String child) {
|
||||||
|
this(new File(parent,child),new HashMap<>());
|
||||||
|
}
|
||||||
|
public EasyConfig(File parent,String child) {
|
||||||
|
this(new File(parent,child),new HashMap<>());
|
||||||
|
}
|
||||||
|
public EasyConfig(Path parent,String child) {
|
||||||
|
this(new File(parent.toFile(),child),new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
data = read();
|
||||||
|
}
|
||||||
|
public void save() {
|
||||||
|
write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toText(Map<String,String> p) {
|
||||||
|
StringBuilder t = new StringBuilder();
|
||||||
|
for (Map.Entry<String,String> e:p.entrySet())
|
||||||
|
t.append(e.getKey()).append("=").append(e.getValue()).append("\n");
|
||||||
|
return t.toString();
|
||||||
|
}
|
||||||
|
private Map<String,String> toMap(String j) {
|
||||||
|
Map<String,String> m = new HashMap<>();
|
||||||
|
for (String l:j.split("\n")) {
|
||||||
|
String s[] = l.split("=");
|
||||||
|
m.put(s[0],s[1]);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String,String> read() {
|
||||||
|
try {
|
||||||
|
return toMap(Files.readString(path));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
private void write(Map<String,String> p) {
|
||||||
|
try {
|
||||||
|
Files.write(path, toText(p).getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,22 @@ import com.google.common.collect.Lists;
|
|||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||||
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
import net.fabricmc.fabric.api.client.rendering.v1.DimensionRenderingRegistry;
|
||||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
|
||||||
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
|
|
||||||
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.render.*;
|
||||||
import net.minecraft.client.util.InputUtil;
|
import net.minecraft.client.util.InputUtil;
|
||||||
import net.minecraft.entity.MovementType;
|
import net.minecraft.entity.MovementType;
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.registry.Registry;
|
import net.minecraft.registry.Registry;
|
||||||
import net.minecraft.resource.Resource;
|
import net.minecraft.registry.RegistryKey;
|
||||||
import net.minecraft.resource.ResourceManager;
|
import net.minecraft.registry.RegistryKeys;
|
||||||
import net.minecraft.resource.ResourceType;
|
import net.minecraft.text.MutableText;
|
||||||
|
import net.minecraft.text.Style;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.text.TextColor;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
@ -25,12 +27,16 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import themixray.repeating.mod.render.RenderHelper;
|
||||||
|
import themixray.repeating.mod.render.RenderSystem;
|
||||||
|
import themixray.repeating.mod.render.buffer.WorldBuffer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.awt.*;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class RepeatingMod implements ClientModInitializer {
|
public class RepeatingMod implements ClientModInitializer {
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod");
|
public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod");
|
||||||
@ -38,22 +44,29 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
public static final FabricLoader loader = FabricLoader.getInstance();
|
public static final FabricLoader loader = FabricLoader.getInstance();
|
||||||
public static RepeatingMod me;
|
public static RepeatingMod me;
|
||||||
|
|
||||||
|
public Vec3d start_record_pos = null;
|
||||||
|
public Vec3d finish_record_pos = null;
|
||||||
|
|
||||||
public List<RecordEvent> record = new ArrayList<>();
|
public List<RecordEvent> record = new ArrayList<>();
|
||||||
public boolean is_recording = false;
|
public boolean is_recording = false;
|
||||||
public Date last_record = null;
|
public long last_record = -1;
|
||||||
|
public TickTask move_tick = null;
|
||||||
|
|
||||||
public Thread replay = null;
|
public TickTask replay_tick = null;
|
||||||
public boolean is_replaying = false;
|
public boolean is_replaying = false;
|
||||||
public boolean loop_replay = false;
|
public boolean loop_replay = false;
|
||||||
public static boolean replay_sneaking = false;
|
public static RecordInputEvent input_replay = null;
|
||||||
|
|
||||||
|
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 double record_blocks_limit = 2;
|
public long record_pos_delay = 20;
|
||||||
public long record_time_limit = 50;
|
|
||||||
|
public static Random rand = new Random();
|
||||||
|
|
||||||
public EasyConfig conf;
|
public EasyConfig conf;
|
||||||
|
|
||||||
@ -62,13 +75,56 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
LOGGER.info("Repeating mod initialized");
|
LOGGER.info("Repeating mod initialized");
|
||||||
me = this;
|
me = this;
|
||||||
|
|
||||||
Map<String,Object> def = new HashMap<>();
|
RenderSystem.init();
|
||||||
def.put("record_blocks_limit", record_blocks_limit);
|
WorldRenderEvents.LAST.register(context -> {
|
||||||
def.put("record_time_limit", record_time_limit);
|
WorldBuffer buffer = RenderHelper.startTri(context);
|
||||||
conf = new EasyConfig(new File(loader.getConfigDir().toFile(),"repeating-mod").toPath(),def);
|
if (start_record_pos != null) {
|
||||||
|
RenderHelper.drawRectFromTri(buffer,
|
||||||
|
(float) start_record_pos.getX() - 0.25F,
|
||||||
|
(float) start_record_pos.getY() + 0.01F,
|
||||||
|
(float) start_record_pos.getZ() - 0.25F,
|
||||||
|
|
||||||
record_blocks_limit = (double) conf.data.get("record_blocks_limit");
|
(float) start_record_pos.getX() + 0.25F,
|
||||||
record_time_limit = (long) conf.data.get("record_time_limit");
|
(float) start_record_pos.getY() + 0.01F,
|
||||||
|
(float) start_record_pos.getZ() - 0.25F,
|
||||||
|
|
||||||
|
(float) start_record_pos.getX() + 0.25F,
|
||||||
|
(float) start_record_pos.getY() + 0.01F,
|
||||||
|
(float) start_record_pos.getZ() + 0.25F,
|
||||||
|
|
||||||
|
(float) start_record_pos.getX() - 0.25F,
|
||||||
|
(float) start_record_pos.getY() + 0.01F,
|
||||||
|
(float) start_record_pos.getZ() + 0.25F,
|
||||||
|
new Color(70,230,70,128));
|
||||||
|
}
|
||||||
|
if (finish_record_pos != null) {
|
||||||
|
RenderHelper.drawRectFromTri(buffer,
|
||||||
|
(float) finish_record_pos.getX() - 0.25F,
|
||||||
|
(float) finish_record_pos.getY() + 0.01F,
|
||||||
|
(float) finish_record_pos.getZ() - 0.25F,
|
||||||
|
|
||||||
|
(float) finish_record_pos.getX() + 0.25F,
|
||||||
|
(float) finish_record_pos.getY() + 0.01F,
|
||||||
|
(float) finish_record_pos.getZ() - 0.25F,
|
||||||
|
|
||||||
|
(float) finish_record_pos.getX() + 0.25F,
|
||||||
|
(float) finish_record_pos.getY() + 0.01F,
|
||||||
|
(float) finish_record_pos.getZ() + 0.25F,
|
||||||
|
|
||||||
|
(float) finish_record_pos.getX() - 0.25F,
|
||||||
|
(float) finish_record_pos.getY() + 0.01F,
|
||||||
|
(float) finish_record_pos.getZ() + 0.25F,
|
||||||
|
new Color(230,70,70,128));
|
||||||
|
}
|
||||||
|
RenderHelper.endTri(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String,String> def = new HashMap<>();
|
||||||
|
def.put("record_pos_delay", String.valueOf(record_pos_delay));
|
||||||
|
|
||||||
|
conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def);
|
||||||
|
|
||||||
|
record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay"));
|
||||||
|
|
||||||
menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
|
menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
|
||||||
"key.repeating-mod.menu",InputUtil.Type.KEYSYM,
|
"key.repeating-mod.menu",InputUtil.Type.KEYSYM,
|
||||||
@ -82,9 +138,8 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
|
|
||||||
menu = new RepeatingScreen();
|
menu = new RepeatingScreen();
|
||||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||||
if (menu_key.wasPressed()) {
|
if (menu_key.wasPressed())
|
||||||
client.setScreen(menu);
|
client.setScreen(menu);
|
||||||
}
|
|
||||||
if (toggle_replay_key.wasPressed()) {
|
if (toggle_replay_key.wasPressed()) {
|
||||||
if (!is_recording) {
|
if (!is_recording) {
|
||||||
if (is_replaying)
|
if (is_replaying)
|
||||||
@ -102,6 +157,13 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
new TickTask(0,0) {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
living_ticks++;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecordEvent getLastRecord(String t) {
|
public RecordEvent getLastRecord(String t) {
|
||||||
@ -113,28 +175,131 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void startRecording() {
|
public void startRecording() {
|
||||||
is_recording = true;
|
is_recording = true;
|
||||||
menu.update_btns();
|
menu.update_btns();
|
||||||
record.clear();
|
record.clear();
|
||||||
|
|
||||||
|
finish_record_pos = null;
|
||||||
|
Vec3d v = client.player.getPos();
|
||||||
|
record.add(new RecordMoveEvent(v,client.player.getHeadYaw(),client.player.getPitch()));
|
||||||
|
start_record_pos = v;
|
||||||
|
|
||||||
|
if (record_pos_delay > 0) {
|
||||||
|
move_tick = new TickTask(
|
||||||
|
record_pos_delay,
|
||||||
|
record_pos_delay) {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
record.add(new RecordMoveEvent(client.player.getPos(),
|
||||||
|
client.player.getHeadYaw(), client.player.getPitch()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
sendMessage(Text.translatable("message.repeating-mod.record_start"));
|
sendMessage(Text.translatable("message.repeating-mod.record_start"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recordTick(RecordEvent e) {
|
public void recordTick(RecordEvent e) {
|
||||||
Date now = new Date();
|
if (is_recording) {
|
||||||
if (last_record != null) {
|
long now = living_ticks;
|
||||||
long diff = now.getTime() - last_record.getTime();
|
if (last_record != -1) {
|
||||||
if (diff >= 0) record.add(new RecordDelayEvent(diff));
|
long diff = now - last_record - 2;
|
||||||
|
if (diff > 0) record.add(new RecordDelayEvent(diff));
|
||||||
|
}
|
||||||
|
record.add(e);
|
||||||
|
last_record = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordAllInput() {
|
||||||
|
RecordInputEvent l = ((RecordInputEvent)getLastRecord("input"));
|
||||||
|
if (l == null) {
|
||||||
|
RecordInputEvent e = new RecordInputEvent(
|
||||||
|
client.player.input.sneaking,
|
||||||
|
client.player.input.jumping,
|
||||||
|
client.player.input.movementSideways,
|
||||||
|
client.player.input.movementForward,
|
||||||
|
client.player.input.pressingForward,
|
||||||
|
client.player.input.pressingBack,
|
||||||
|
client.player.input.pressingLeft,
|
||||||
|
client.player.input.pressingRight,
|
||||||
|
client.player.getHeadYaw(),
|
||||||
|
client.player.getBodyYaw(),
|
||||||
|
client.player.getPitch(),
|
||||||
|
client.player.isSprinting(),
|
||||||
|
client.player.getYaw(),
|
||||||
|
client.player.getMovementSpeed());
|
||||||
|
recordTick(e);
|
||||||
|
} else {
|
||||||
|
RecordInputEvent e = new RecordInputEvent(
|
||||||
|
((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking,
|
||||||
|
((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping,
|
||||||
|
(((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways,
|
||||||
|
(((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward,
|
||||||
|
((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward,
|
||||||
|
((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack,
|
||||||
|
((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft,
|
||||||
|
((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight,
|
||||||
|
client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(),
|
||||||
|
((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(),
|
||||||
|
client.player.getYaw(),client.player.getMovementSpeed());
|
||||||
|
|
||||||
|
if (!(e.isEmpty() &&
|
||||||
|
e.yaw == l.yaw &&
|
||||||
|
e.head_yaw == l.head_yaw &&
|
||||||
|
e.pitch == l.pitch &&
|
||||||
|
e.body_yaw == l.body_yaw)) {
|
||||||
|
e.fillEmpty(l);
|
||||||
|
recordTick(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recordCameraInput() {
|
||||||
|
RecordInputEvent l = ((RecordInputEvent)getLastRecord("input"));
|
||||||
|
if (l == null) {
|
||||||
|
RecordInputEvent e = new RecordInputEvent(
|
||||||
|
client.player.input.sneaking,
|
||||||
|
client.player.input.jumping,
|
||||||
|
client.player.input.movementSideways,
|
||||||
|
client.player.input.movementForward,
|
||||||
|
client.player.input.pressingForward,
|
||||||
|
client.player.input.pressingBack,
|
||||||
|
client.player.input.pressingLeft,
|
||||||
|
client.player.input.pressingRight,
|
||||||
|
client.player.getHeadYaw(),
|
||||||
|
client.player.getBodyYaw(),
|
||||||
|
client.player.getPitch(),
|
||||||
|
client.player.isSprinting(),
|
||||||
|
client.player.getYaw(),
|
||||||
|
client.player.getMovementSpeed());
|
||||||
|
recordTick(e);
|
||||||
|
} else {
|
||||||
|
RecordInputEvent e = new RecordInputEvent(null,null,null,
|
||||||
|
null,null,null,null,null,
|
||||||
|
client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(),
|
||||||
|
null,client.player.getYaw(),client.player.getMovementSpeed());
|
||||||
|
|
||||||
|
if (!(e.yaw == l.yaw &&
|
||||||
|
e.head_yaw == l.head_yaw &&
|
||||||
|
e.pitch == l.pitch &&
|
||||||
|
e.body_yaw == l.body_yaw)) {
|
||||||
|
e.fillEmpty(l);
|
||||||
|
recordTick(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
record.add(e);
|
|
||||||
last_record = now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRecording() {
|
public void stopRecording() {
|
||||||
is_recording = false;
|
is_recording = false;
|
||||||
|
finish_record_pos = client.player.getPos();
|
||||||
|
if (move_tick != null) {
|
||||||
|
move_tick.cancel();
|
||||||
|
move_tick = null;
|
||||||
|
}
|
||||||
menu.update_btns();
|
menu.update_btns();
|
||||||
last_record = null;
|
last_record = -1;
|
||||||
sendMessage(Text.translatable("message.repeating-mod.record_stop"));
|
sendMessage(Text.translatable("message.repeating-mod.record_stop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,25 +308,42 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
is_recording = false;
|
is_recording = false;
|
||||||
is_replaying = true;
|
is_replaying = true;
|
||||||
menu.update_btns();
|
menu.update_btns();
|
||||||
client.player.setNoGravity(true);
|
replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) {
|
||||||
replay = new Thread(() -> {
|
public int replay_index = 0;
|
||||||
while (true) {
|
|
||||||
for (RecordEvent e : record)
|
@Override
|
||||||
if (is_replaying)
|
public void run() {
|
||||||
e.callback();
|
if (!is_replaying) cancel();
|
||||||
if (!loop_replay || !is_replaying) break;
|
RecordEvent e = record.get(replay_index);
|
||||||
|
if (e instanceof RecordDelayEvent) {
|
||||||
|
setDelay(((RecordDelayEvent) e).delay);
|
||||||
|
} else {
|
||||||
|
e.callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
replay_index++;
|
||||||
|
if (!loop_replay) {
|
||||||
|
if (replay_index == record.size()) {
|
||||||
|
stopReplay();
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
} else if (replay_index == record.size()) {
|
||||||
|
replay_index = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stopReplay();
|
};
|
||||||
});
|
|
||||||
replay.start();
|
|
||||||
sendMessage(Text.translatable("message.repeating-mod.replay_start"));
|
sendMessage(Text.translatable("message.repeating-mod.replay_start"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopReplay() {
|
public void stopReplay() {
|
||||||
is_recording = false;
|
is_recording = false;
|
||||||
is_replaying = false;
|
is_replaying = false;
|
||||||
|
if (replay_tick != null) {
|
||||||
|
replay_tick.cancel();
|
||||||
|
replay_tick = null;
|
||||||
|
}
|
||||||
menu.update_btns();
|
menu.update_btns();
|
||||||
client.player.setNoGravity(false);
|
|
||||||
sendMessage(Text.translatable("message.repeating-mod.replay_stop"));
|
sendMessage(Text.translatable("message.repeating-mod.replay_stop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,10 +353,15 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
return (double) Math.round(value * factor) / factor;
|
return (double) Math.round(value * factor) / factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendMessage(Text text) {
|
public static void sendMessage(MutableText text) {
|
||||||
client.player.sendMessage(Text.literal("[")
|
client.player.sendMessage(Text.literal("[")
|
||||||
.append(Text.translatable("text.repeating-mod.name"))
|
.append(Text.translatable("text.repeating-mod.name"))
|
||||||
.append("] ").append(text));
|
.append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY)
|
||||||
|
.append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendDebug(String s) {
|
||||||
|
client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class RecordEvent {
|
public static abstract class RecordEvent {
|
||||||
@ -187,36 +374,15 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
String type = String.valueOf(t.charAt(0));
|
String type = String.valueOf(t.charAt(0));
|
||||||
String[] args = t.substring(2).split("&");
|
String[] args = t.substring(2).split("&");
|
||||||
if (type.equals("d")) {
|
if (type.equals("d")) {
|
||||||
return new RecordDelayEvent(
|
return RecordDelayEvent.fromArgs(args);
|
||||||
Long.parseLong(args[0]));
|
|
||||||
} else if (type.equals("m")) {
|
} else if (type.equals("m")) {
|
||||||
return new RecordMoveEvent(new Vec3d(
|
return RecordMoveEvent.fromArgs(args);
|
||||||
Double.parseDouble(args[0]),
|
} else if (type.equals("p")) {
|
||||||
Double.parseDouble(args[1]),
|
return RecordInputEvent.fromArgs(args);
|
||||||
Double.parseDouble(args[2])),
|
|
||||||
Float.parseFloat(args[3]),
|
|
||||||
Float.parseFloat(args[4]));
|
|
||||||
} else if (type.equals("s")) {
|
|
||||||
return new RecordSneakEvent(
|
|
||||||
args[0].equals("1"));
|
|
||||||
} else if (type.equals("b")) {
|
} else if (type.equals("b")) {
|
||||||
return new RecordBlockBreakEvent(new BlockPos(
|
return RecordBlockBreakEvent.fromArgs(args);
|
||||||
Integer.parseInt(args[0]),
|
|
||||||
Integer.parseInt(args[1]),
|
|
||||||
Integer.parseInt(args[2])));
|
|
||||||
} else if (type.equals("i")) {
|
} else if (type.equals("i")) {
|
||||||
return new RecordBlockInteractEvent(
|
return RecordBlockInteractEvent.fromArgs(args);
|
||||||
Hand.valueOf(args[5]),
|
|
||||||
new BlockHitResult(new Vec3d(
|
|
||||||
Double.parseDouble(args[0]),
|
|
||||||
Double.parseDouble(args[1]),
|
|
||||||
Double.parseDouble(args[2])),
|
|
||||||
Direction.byId(Integer.parseInt(args[4])),
|
|
||||||
new BlockPos(
|
|
||||||
Integer.parseInt(args[0]),
|
|
||||||
Integer.parseInt(args[1]),
|
|
||||||
Integer.parseInt(args[2])),
|
|
||||||
args[3].equals("1")));
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -228,13 +394,17 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
public static class RecordDelayEvent extends RecordEvent {
|
public static class RecordDelayEvent extends RecordEvent {
|
||||||
public long delay;
|
public long delay;
|
||||||
|
|
||||||
|
public static RecordDelayEvent fromArgs(String[] a) {
|
||||||
|
return new RecordDelayEvent(Long.parseLong(a[0]));
|
||||||
|
}
|
||||||
|
|
||||||
public RecordDelayEvent(long delay) {
|
public RecordDelayEvent(long delay) {
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void callback() {
|
public void callback() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(delay);
|
Thread.sleep(delay/20*1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -253,6 +423,15 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
public float yaw;
|
public float yaw;
|
||||||
public float pitch;
|
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) {
|
public RecordMoveEvent(Vec3d vec,float yaw,float pitch) {
|
||||||
this.vec = vec;
|
this.vec = vec;
|
||||||
this.yaw = yaw;
|
this.yaw = yaw;
|
||||||
@ -275,28 +454,160 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecordSneakEvent extends RecordEvent {
|
public static class RecordInputEvent extends RecordEvent {
|
||||||
public boolean sneaking;
|
public Boolean sneaking;
|
||||||
|
public Boolean jumping;
|
||||||
|
public Boolean pressingForward;
|
||||||
|
public Boolean pressingBack;
|
||||||
|
public Boolean pressingLeft;
|
||||||
|
public Boolean pressingRight;
|
||||||
|
public Boolean sprinting;
|
||||||
|
|
||||||
public RecordSneakEvent(boolean sneaking) {
|
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.sneaking = sneaking;
|
||||||
|
this.jumping = jumping;
|
||||||
|
this.movementSideways = movementSideways;
|
||||||
|
this.movementForward = movementForward;
|
||||||
|
this.pressingForward = pressingForward;
|
||||||
|
this.pressingBack = pressingBack;
|
||||||
|
this.pressingLeft = pressingLeft;
|
||||||
|
this.pressingRight = pressingRight;
|
||||||
|
this.head_yaw = head_yaw;
|
||||||
|
this.body_yaw = body_yaw;
|
||||||
|
this.pitch = head_pitch;
|
||||||
|
this.sprinting = sprinting;
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillEmpty(RecordInputEvent e) {
|
||||||
|
if (sneaking == null) sneaking = e.sneaking;
|
||||||
|
if (jumping == null) jumping = e.jumping;
|
||||||
|
if (movementSideways == null) movementSideways = e.movementSideways;
|
||||||
|
if (movementForward == null) movementForward = e.movementForward;
|
||||||
|
if (pressingForward == null) pressingForward = e.pressingForward;
|
||||||
|
if (pressingBack == null) pressingBack = e.pressingBack;
|
||||||
|
if (pressingLeft == null) pressingLeft = e.pressingLeft;
|
||||||
|
if (pressingRight == null) pressingRight = e.pressingRight;
|
||||||
|
if (sprinting == null) sprinting = e.sprinting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return sneaking == null &&
|
||||||
|
jumping == null &&
|
||||||
|
movementSideways == null &&
|
||||||
|
movementForward == null &&
|
||||||
|
pressingForward == null &&
|
||||||
|
pressingBack == null &&
|
||||||
|
pressingLeft == null &&
|
||||||
|
pressingRight == null &&
|
||||||
|
sprinting == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void callback() {
|
public void callback() {
|
||||||
RepeatingMod.replay_sneaking = sneaking;
|
input_replay = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inputCallback() {
|
||||||
|
if (sprinting != null && client.player.isSprinting() != sprinting)
|
||||||
|
client.player.setSprinting(sprinting);
|
||||||
|
if (client.player.getYaw() != yaw)
|
||||||
|
client.player.setYaw(yaw);
|
||||||
|
if (client.player.getHeadYaw() != head_yaw)
|
||||||
|
client.player.setHeadYaw(head_yaw);
|
||||||
|
if (client.player.getBodyYaw() != body_yaw)
|
||||||
|
client.player.setBodyYaw(body_yaw);
|
||||||
|
if (client.player.getPitch() != pitch)
|
||||||
|
client.player.setPitch(pitch);
|
||||||
|
if (client.player.getMovementSpeed() != speed)
|
||||||
|
client.player.setMovementSpeed(speed);
|
||||||
|
if (sneaking != null && client.player.input.sneaking != sneaking)
|
||||||
|
client.player.input.sneaking = sneaking;
|
||||||
|
if (jumping != null && client.player.input.jumping != jumping)
|
||||||
|
client.player.input.jumping = jumping;
|
||||||
|
if (movementSideways != null && client.player.input.movementSideways != movementSideways)
|
||||||
|
client.player.input.movementSideways = movementSideways;
|
||||||
|
if (movementForward != null && client.player.input.movementForward != movementForward)
|
||||||
|
client.player.input.movementForward = movementForward;
|
||||||
|
if (pressingForward != null && client.player.input.pressingForward != pressingForward)
|
||||||
|
client.player.input.pressingForward = pressingForward;
|
||||||
|
if (pressingBack != null && client.player.input.pressingBack != pressingBack)
|
||||||
|
client.player.input.pressingBack = pressingBack;
|
||||||
|
if (pressingLeft != null && client.player.input.pressingLeft != pressingLeft)
|
||||||
|
client.player.input.pressingLeft = pressingLeft;
|
||||||
|
if (pressingRight != null && client.player.input.pressingRight != pressingRight)
|
||||||
|
client.player.input.pressingRight = pressingRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toText() {
|
public String toText() {
|
||||||
return "s="+(sneaking?"1":"0");
|
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() {
|
public String getType() {
|
||||||
return "sneak";
|
return "input";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecordBlockBreakEvent extends RecordEvent {
|
public static class RecordBlockBreakEvent extends RecordEvent {
|
||||||
public BlockPos pos;
|
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(
|
public RecordBlockBreakEvent(
|
||||||
BlockPos pos) {
|
BlockPos pos) {
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
@ -318,6 +629,21 @@ public class RepeatingMod implements ClientModInitializer {
|
|||||||
public Hand hand;
|
public Hand hand;
|
||||||
public BlockHitResult hitResult;
|
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) {
|
public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.hitResult = hitResult;
|
this.hitResult = hitResult;
|
||||||
|
371
src/main/java/themixray/repeating/mod/RepeatingScreen.java
Normal file
371
src/main/java/themixray/repeating/mod/RepeatingScreen.java
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
package themixray.repeating.mod;
|
||||||
|
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.client.gui.DrawContext;
|
||||||
|
import net.minecraft.client.gui.screen.Screen;
|
||||||
|
import net.minecraft.client.gui.tooltip.Tooltip;
|
||||||
|
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||||
|
import net.minecraft.client.gui.widget.OptionSliderWidget;
|
||||||
|
import net.minecraft.client.gui.widget.SliderWidget;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Environment(EnvType.CLIENT)
|
||||||
|
public class RepeatingScreen extends Screen {
|
||||||
|
protected RepeatingScreen() {
|
||||||
|
super(Text.literal(""));
|
||||||
|
this.mod = RepeatingMod.me;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepeatingMod mod;
|
||||||
|
|
||||||
|
public ButtonWidget record_btn;
|
||||||
|
public ButtonWidget replay_btn;
|
||||||
|
public ButtonWidget loop_btn;
|
||||||
|
|
||||||
|
public ButtonWidget export_btn;
|
||||||
|
public ButtonWidget import_btn;
|
||||||
|
|
||||||
|
public SliderWidget pos_delay_slider;
|
||||||
|
|
||||||
|
public boolean was_build = false;
|
||||||
|
|
||||||
|
public void update_btns() {
|
||||||
|
if (was_build) {
|
||||||
|
replay_btn.setMessage(Text.translatable("text.repeating-mod." +
|
||||||
|
((mod.is_replaying) ? "stop_replay" : "start_replay")));
|
||||||
|
record_btn.setMessage(Text.translatable("text.repeating-mod." +
|
||||||
|
((mod.is_recording) ? "stop_record" : "start_record")));
|
||||||
|
loop_btn.setMessage(Text.of(((mod.loop_replay) ? "\uefff " : "\ueffe ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
renderBackgroundTexture(context);
|
||||||
|
// context.drawCenteredTextWithShadow(textRenderer,
|
||||||
|
// Text.literal("You must see me"),
|
||||||
|
// width / 2, height / 2,
|
||||||
|
// Color.white.getRGB());
|
||||||
|
super.render(context, mouseX, mouseY, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
record_btn = ButtonWidget.builder(
|
||||||
|
Text.translatable("text.repeating-mod.start_record"), button -> {
|
||||||
|
if (!mod.is_replaying) {
|
||||||
|
if (mod.is_recording)
|
||||||
|
mod.stopRecording();
|
||||||
|
else mod.startRecording();
|
||||||
|
update_btns();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.dimensions(width / 2 - 60, height / 2 - 54, 120, 20)
|
||||||
|
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
replay_btn = ButtonWidget.builder(
|
||||||
|
Text.translatable("text.repeating-mod.start_replay"), button -> {
|
||||||
|
if (!mod.is_recording) {
|
||||||
|
if (mod.is_replaying)
|
||||||
|
mod.stopReplay();
|
||||||
|
else mod.startReplay();
|
||||||
|
update_btns();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.dimensions(width / 2 - 60, height / 2 - 32, 98, 20)
|
||||||
|
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
loop_btn = ButtonWidget.builder(Text.of(""), button -> {
|
||||||
|
mod.loop_replay = !mod.loop_replay;
|
||||||
|
update_btns();
|
||||||
|
})
|
||||||
|
.dimensions(width / 2 + 40, height / 2 - 32, 20, 20)
|
||||||
|
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
export_btn = ButtonWidget.builder(
|
||||||
|
Text.translatable("text.repeating-mod.export"), button -> {
|
||||||
|
if (mod.finish_record_pos == null) return;
|
||||||
|
StringBuilder t = new StringBuilder();
|
||||||
|
for (int i = 0; i < mod.record.size(); i++) {
|
||||||
|
t.append(mod.record.get(i).toText());
|
||||||
|
t.append("\n");
|
||||||
|
}
|
||||||
|
t.append(mod.start_record_pos.getX()+"n"+
|
||||||
|
mod.start_record_pos.getY()+"n"+
|
||||||
|
mod.start_record_pos.getZ()+"x"+
|
||||||
|
mod.finish_record_pos.getX()+"n"+
|
||||||
|
mod.finish_record_pos.getY()+"n"+
|
||||||
|
mod.finish_record_pos.getZ());
|
||||||
|
|
||||||
|
File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
|
||||||
|
if (!p.exists()) p.mkdir();
|
||||||
|
File file = new File(p,"export_"+
|
||||||
|
new SimpleDateFormat("MM_dd_yyyy").format(new Date())
|
||||||
|
+"_"+RepeatingMod.rand.nextInt(10)+".txt");
|
||||||
|
try {
|
||||||
|
if (!file.exists()) file.createNewFile();
|
||||||
|
Files.write(file.toPath(), t.toString().getBytes());
|
||||||
|
Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.dimensions(width / 2 - 60, height / 2 - 10, 120, 20)
|
||||||
|
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.export_tooltip")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
import_btn = ButtonWidget.builder(
|
||||||
|
Text.translatable("text.repeating-mod.import"), button -> {
|
||||||
|
mod.record.clear();
|
||||||
|
|
||||||
|
File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
|
||||||
|
if (!p.exists()) p.mkdir();
|
||||||
|
File file = new File(p,"import.txt");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String t = Files.readString(file.toPath());
|
||||||
|
List<String> ss = List.of(t.split("\n"));
|
||||||
|
String ls = ss.get(ss.size()-1);
|
||||||
|
ss = ss.subList(0,ss.size()-1);
|
||||||
|
for (String s:ss)
|
||||||
|
mod.record.add(RepeatingMod.RecordEvent.fromText(s));
|
||||||
|
String[] lss0 = ls.split("x");
|
||||||
|
String[] lss1 = lss0[0].split("n");
|
||||||
|
String[] lss2 = lss0[1].split("n");
|
||||||
|
mod.start_record_pos = new Vec3d(
|
||||||
|
Float.parseFloat(lss1[0]),
|
||||||
|
Float.parseFloat(lss1[1]),
|
||||||
|
Float.parseFloat(lss1[2]));
|
||||||
|
mod.finish_record_pos = new Vec3d(
|
||||||
|
Float.parseFloat(lss2[0]),
|
||||||
|
Float.parseFloat(lss2[1]),
|
||||||
|
Float.parseFloat(lss2[2]));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.dimensions(width / 2 - 60, height / 2 + 12, 120, 20)
|
||||||
|
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip")))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
pos_delay_slider = new SliderWidget(
|
||||||
|
width / 2 - 60, height / 2 + 34, 120, 20,
|
||||||
|
(mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") :
|
||||||
|
Text.translatable("text.repeating-mod.pos_delay", String.valueOf(mod.record_pos_delay)),
|
||||||
|
(mod.record_pos_delay/10d+1d)/101d) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateMessage() {
|
||||||
|
double v = value*101d-1d;
|
||||||
|
if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay"));
|
||||||
|
else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void applyValue() {
|
||||||
|
double v = value*101d-1d;
|
||||||
|
if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay"));
|
||||||
|
else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10))));
|
||||||
|
mod.record_pos_delay = (long) (v*10);
|
||||||
|
mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay));
|
||||||
|
mod.conf.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRelease(double mouseX, double mouseY) {
|
||||||
|
super.onRelease(mouseX, mouseY);
|
||||||
|
applyValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) {
|
||||||
|
super.onDrag(mouseX, mouseY, deltaX, deltaY);
|
||||||
|
applyValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
super.render(context, mouseX, mouseY, delta);
|
||||||
|
updateMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip")));
|
||||||
|
|
||||||
|
was_build = true;
|
||||||
|
|
||||||
|
update_btns();
|
||||||
|
|
||||||
|
addDrawableChild(replay_btn);
|
||||||
|
addDrawableChild(loop_btn);
|
||||||
|
addDrawableChild(record_btn);
|
||||||
|
addDrawableChild(export_btn);
|
||||||
|
addDrawableChild(import_btn);
|
||||||
|
addDrawableChild(pos_delay_slider);
|
||||||
|
|
||||||
|
|
||||||
|
// rootComponent
|
||||||
|
// .surface(Surface.VANILLA_TRANSLUCENT)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER);
|
||||||
|
//
|
||||||
|
// replay_btn = (ButtonComponent) Components.button(Text.of("replay"),
|
||||||
|
// (ButtonComponent btn) -> {
|
||||||
|
// if (!mod.is_recording) {
|
||||||
|
// if (mod.is_replaying)
|
||||||
|
// mod.stopReplay();
|
||||||
|
// else mod.startReplay();
|
||||||
|
// update_btns();
|
||||||
|
// }
|
||||||
|
// }).margins(Insets.of(1)).sizing(
|
||||||
|
// Sizing.fixed(98),Sizing.fixed(20));
|
||||||
|
//
|
||||||
|
// loop_btn = (ButtonComponent) Components.button(Text.of(""),
|
||||||
|
// (ButtonComponent btn) -> {
|
||||||
|
// mod.loop_replay = !mod.loop_replay;
|
||||||
|
// update_btns();
|
||||||
|
// }).margins(Insets.of(1))
|
||||||
|
// .sizing(Sizing.fixed(20),Sizing.fixed(20));
|
||||||
|
//
|
||||||
|
// record_btn = (ButtonComponent) Components.button(Text.of("record"),
|
||||||
|
// (ButtonComponent btn) -> {
|
||||||
|
// if (!mod.is_replaying) {
|
||||||
|
// if (mod.is_recording)
|
||||||
|
// mod.stopRecording();
|
||||||
|
// else mod.startRecording();
|
||||||
|
// update_btns();
|
||||||
|
// }
|
||||||
|
// }).margins(Insets.of(1)).sizing(
|
||||||
|
// Sizing.fixed(120),Sizing.fixed(20));
|
||||||
|
// was_build = true;
|
||||||
|
//
|
||||||
|
// rootComponent.child(
|
||||||
|
// Containers.horizontalFlow(Sizing.content(), Sizing.content()).child(
|
||||||
|
// Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Components.label(Text.translatable("text.repeating-mod.basic")).margins(Insets.of(1)))
|
||||||
|
// .padding(Insets.of(5))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Containers.horizontalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(replay_btn).child(loop_btn))
|
||||||
|
// .child(record_btn)
|
||||||
|
// .child(Components.button(Text.translatable(
|
||||||
|
// "text.repeating-mod.export"),
|
||||||
|
// (ButtonComponent btn) -> {
|
||||||
|
// String t = "";
|
||||||
|
// for (int i = 0; i < mod.record.size(); i++) {
|
||||||
|
// t += mod.record.get(i).toText();
|
||||||
|
// if (i != mod.record.size()-1)
|
||||||
|
// t += "\n";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
|
||||||
|
// if (!p.exists()) p.mkdir();
|
||||||
|
// File file = new File(p,"export.txt");
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// if (!file.exists()) file.createNewFile();
|
||||||
|
// Files.write(file.toPath(), t.getBytes());
|
||||||
|
// Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }).margins(Insets.of(10,1,1,1)).sizing(
|
||||||
|
// Sizing.fixed(120),Sizing.fixed(20)))
|
||||||
|
// .child(Components.button(Text.translatable(
|
||||||
|
// "text.repeating-mod.import"),
|
||||||
|
// (ButtonComponent btn) -> {
|
||||||
|
// mod.record.clear();
|
||||||
|
//
|
||||||
|
// File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
|
||||||
|
// if (!p.exists()) p.mkdir();
|
||||||
|
// File file = new File(p,"import.txt");
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// if (!file.exists()) {
|
||||||
|
// file.createNewFile();
|
||||||
|
// Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// String t = Files.readString(file.toPath());
|
||||||
|
// for (String s:t.split("\n"))
|
||||||
|
// mod.record.add(RepeatingMod.RecordEvent.fromText(s));
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }).margins(Insets.of(1)).sizing(
|
||||||
|
// Sizing.fixed(120),Sizing.fixed(20)))
|
||||||
|
// .padding(Insets.of(10))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))
|
||||||
|
// /*).child(
|
||||||
|
// Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Components.label(Text.translatable("text.repeating-mod.parkour")).margins(Insets.of(1)))
|
||||||
|
// .padding(Insets.of(5))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Components.label(Text.translatable("text.repeating-mod.dev")).margins(Insets.of(1)))
|
||||||
|
// .padding(Insets.of(10))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))*/
|
||||||
|
// ).child(
|
||||||
|
// Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Components.label(Text.translatable("text.repeating-mod.settings")).margins(Insets.of(1)))
|
||||||
|
// .padding(Insets.of(5))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))
|
||||||
|
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
|
||||||
|
// .child(Components.discreteSlider(Sizing.fixed(120),-20,100)
|
||||||
|
// .setFromDiscreteValue(mod.record_pos_delay)
|
||||||
|
// .message((String s)->{
|
||||||
|
// mod.record_pos_delay = Long.parseLong(s);
|
||||||
|
// mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay));
|
||||||
|
// mod.conf.save();
|
||||||
|
// if (mod.record_pos_delay > -1)
|
||||||
|
// return Text.translatable("text.repeating-mod.pos_delay", s);
|
||||||
|
// return Text.translatable("text.repeating-mod.nan_pos_delay");
|
||||||
|
// }).scrollStep(25)
|
||||||
|
// .margins(Insets.of(1))
|
||||||
|
// .tooltip(Text.translatable("text.repeating-mod.pos_delay_text")))
|
||||||
|
// .padding(Insets.of(10))
|
||||||
|
// .surface(Surface.DARK_PANEL)
|
||||||
|
// .verticalAlignment(VerticalAlignment.CENTER)
|
||||||
|
// .horizontalAlignment(HorizontalAlignment.CENTER)
|
||||||
|
// .margins(Insets.of(1)))
|
||||||
|
// ));
|
||||||
|
update_btns();
|
||||||
|
}
|
||||||
|
}
|
94
src/main/java/themixray/repeating/mod/TickTask.java
Normal file
94
src/main/java/themixray/repeating/mod/TickTask.java
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package themixray.repeating.mod;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class TickTask implements Runnable {
|
||||||
|
public static List<TickTask> tasks = new ArrayList<>();
|
||||||
|
|
||||||
|
public static void tickTasks(TickAt at) {
|
||||||
|
for (TickTask t:new ArrayList<>(tasks))
|
||||||
|
if (t.getAt() == at) t.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long living;
|
||||||
|
private long delay;
|
||||||
|
|
||||||
|
private boolean is_repeating;
|
||||||
|
private long period;
|
||||||
|
|
||||||
|
private boolean is_cancelled;
|
||||||
|
private TickAt at;
|
||||||
|
|
||||||
|
public enum TickAt {
|
||||||
|
CLIENT_HEAD, CLIENT_TAIL,
|
||||||
|
MOVEMENT_HEAD, MOVEMENT_TAIL,
|
||||||
|
RENDER_HEAD, RENDER_TAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickTask(long delay, TickAt at) {
|
||||||
|
this.is_cancelled = false;
|
||||||
|
this.is_repeating = false;
|
||||||
|
this.delay = delay;
|
||||||
|
this.living = 0;
|
||||||
|
this.period = 0;
|
||||||
|
this.at = at;
|
||||||
|
tasks.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickTask(long delay, long period, TickAt at) {
|
||||||
|
this.is_cancelled = false;
|
||||||
|
this.is_repeating = true;
|
||||||
|
this.delay = delay;
|
||||||
|
this.period = period;
|
||||||
|
this.living = 0;
|
||||||
|
this.at = at;
|
||||||
|
tasks.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickTask(long delay) {
|
||||||
|
this(delay,TickAt.CLIENT_HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickTask(long delay, long period) {
|
||||||
|
this(delay,period,TickAt.CLIENT_HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
if (!is_cancelled) {
|
||||||
|
is_cancelled = true;
|
||||||
|
tasks.remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return is_cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickAt getAt() {
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelay(long delay) {
|
||||||
|
if (is_repeating) {
|
||||||
|
this.delay = delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public long getDelay() {
|
||||||
|
return this.delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
if (living >= delay) {
|
||||||
|
if (is_repeating) {
|
||||||
|
delay = period;
|
||||||
|
run();
|
||||||
|
living = -1;
|
||||||
|
} else {
|
||||||
|
run();
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
living++;
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/themixray/repeating/mod/mixin/ClientMixin.java
Normal file
24
src/main/java/themixray/repeating/mod/mixin/ClientMixin.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package themixray.repeating.mod.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import themixray.repeating.mod.RepeatingMod;
|
||||||
|
import themixray.repeating.mod.TickTask;
|
||||||
|
|
||||||
|
@Mixin(MinecraftClient.class)
|
||||||
|
public abstract class ClientMixin {
|
||||||
|
@Inject(at = @At(value = "HEAD"), method = "tick")
|
||||||
|
private void onTickHead(CallbackInfo ci) {
|
||||||
|
if (RepeatingMod.me.is_recording)
|
||||||
|
RepeatingMod.me.recordAllInput();
|
||||||
|
TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At(value = "TAIL"), method = "tick")
|
||||||
|
private void onTickTail(CallbackInfo ci) {
|
||||||
|
TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL);
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/themixray/repeating/mod/mixin/EntityMixin.java
Normal file
31
src/main/java/themixray/repeating/mod/mixin/EntityMixin.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package themixray.repeating.mod.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import themixray.repeating.mod.RepeatingMod;
|
||||||
|
import themixray.repeating.mod.TickTask;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Mixin(Entity.class)
|
||||||
|
public abstract class EntityMixin {
|
||||||
|
@Shadow public abstract UUID getUuid();
|
||||||
|
|
||||||
|
@Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true)
|
||||||
|
private void onSprint(boolean sprinting,CallbackInfo ci) {
|
||||||
|
if (getUuid().equals(RepeatingMod.client.player.getUuid())) {
|
||||||
|
if (RepeatingMod.me.is_replaying) {
|
||||||
|
if (RepeatingMod.input_replay != null &&
|
||||||
|
RepeatingMod.input_replay.sprinting != null &&
|
||||||
|
RepeatingMod.input_replay.sprinting != sprinting) {
|
||||||
|
ci.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/main/java/themixray/repeating/mod/mixin/InputMixin.java
Normal file
20
src/main/java/themixray/repeating/mod/mixin/InputMixin.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package themixray.repeating.mod.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.input.KeyboardInput;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import themixray.repeating.mod.RepeatingMod;
|
||||||
|
|
||||||
|
@Mixin(KeyboardInput.class)
|
||||||
|
public abstract class InputMixin {
|
||||||
|
@Inject(at = @At(value = "TAIL"), method = "tick")
|
||||||
|
private void onTickTail(boolean slowDown, float f, CallbackInfo ci) {
|
||||||
|
if (RepeatingMod.me.is_replaying) {
|
||||||
|
if (RepeatingMod.input_replay != null) {
|
||||||
|
RepeatingMod.input_replay.inputCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,31 +2,20 @@ package themixray.repeating.mod.mixin;
|
|||||||
|
|
||||||
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
|
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
|
||||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
import net.minecraft.entity.MovementType;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import net.minecraft.util.hit.HitResult;
|
import net.minecraft.util.hit.HitResult;
|
||||||
import net.minecraft.util.math.Vec3d;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import themixray.repeating.mod.RepeatingMod;
|
import themixray.repeating.mod.RepeatingMod;
|
||||||
|
import themixray.repeating.mod.TickTask;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Mixin(ClientPlayerEntity.class)
|
@Mixin(ClientPlayerEntity.class)
|
||||||
public abstract class MovementMixin {
|
public abstract class MovementMixin {
|
||||||
public Vec3d lastVec = null;
|
|
||||||
@Shadow public abstract void sendMessage(Text message);
|
|
||||||
@Shadow @Final protected MinecraftClient client;
|
|
||||||
@Shadow public abstract float getYaw(float tickDelta);
|
|
||||||
@Shadow private float lastYaw;
|
|
||||||
@Shadow private float lastPitch;
|
|
||||||
|
|
||||||
@Inject(at = @At(value = "HEAD"), method = "init")
|
@Inject(at = @At(value = "HEAD"), method = "init")
|
||||||
private void init(CallbackInfo ci) {
|
private void init(CallbackInfo ci) {
|
||||||
@ -43,50 +32,13 @@ public abstract class MovementMixin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(at = @At(value = "HEAD"), method = "move")
|
@Inject(at = @At(value = "HEAD"), method = "tickMovement")
|
||||||
private void onMove(MovementType movementType, Vec3d vec, CallbackInfo ci) {
|
private void onMoveHead(CallbackInfo ci) {
|
||||||
if (RepeatingMod.me.is_recording) {
|
TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD);
|
||||||
if (vec != lastVec) {
|
}
|
||||||
double dist = 0;
|
|
||||||
if (lastVec != null)
|
|
||||||
dist = vec.distanceTo(lastVec);
|
|
||||||
if (dist > 0.0) {
|
|
||||||
Vec3d c = client.player.getPos();
|
|
||||||
|
|
||||||
RepeatingMod.RecordMoveEvent ev = new RepeatingMod.RecordMoveEvent(
|
@Inject(at = @At(value = "TAIL"), method = "tick")
|
||||||
new Vec3d(c.getX() + vec.getX(),
|
private void onMoveTail(CallbackInfo ci) {
|
||||||
c.getY() + vec.getY(),
|
TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL);
|
||||||
c.getZ() + vec.getZ()),
|
|
||||||
lastYaw, lastPitch);
|
|
||||||
|
|
||||||
boolean just_add = true;
|
|
||||||
Date now = new Date();
|
|
||||||
if (RepeatingMod.me.last_record != null) {
|
|
||||||
long diff = now.getTime() - RepeatingMod.me.last_record.getTime();
|
|
||||||
boolean add_delay = true;
|
|
||||||
if (diff > 0) {
|
|
||||||
RepeatingMod.RecordEvent last_ev = RepeatingMod.me.record.get(RepeatingMod.me.record.size()-1);
|
|
||||||
if (last_ev instanceof RepeatingMod.RecordMoveEvent) {
|
|
||||||
RepeatingMod.RecordMoveEvent last_ev1 = (RepeatingMod.RecordMoveEvent) last_ev;
|
|
||||||
if (last_ev1.vec.distanceTo(ev.vec) < RepeatingMod.me.record_blocks_limit &&
|
|
||||||
diff < RepeatingMod.me.record_time_limit) {
|
|
||||||
just_add = false;
|
|
||||||
add_delay = false;
|
|
||||||
last_ev1.vec = ev.vec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (add_delay) {
|
|
||||||
RepeatingMod.me.record.add(new RepeatingMod.RecordDelayEvent(diff));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (just_add) {
|
|
||||||
RepeatingMod.me.record.add(ev);
|
|
||||||
RepeatingMod.me.last_record = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastVec = vec;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package themixray.repeating.mod.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.input.KeyboardInput;
|
||||||
|
import net.minecraft.client.render.*;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import themixray.repeating.mod.RepeatingMod;
|
||||||
|
import themixray.repeating.mod.TickTask;
|
||||||
|
|
||||||
|
@Mixin(GameRenderer.class)
|
||||||
|
public abstract class RendererMixin {
|
||||||
|
@Inject(at = @At(value = "HEAD"), method = "tick")
|
||||||
|
private void onTickHead(CallbackInfo ci) {
|
||||||
|
TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At(value = "TAIL"), method = "tick")
|
||||||
|
private void onTickTail(CallbackInfo ci) {
|
||||||
|
TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL);
|
||||||
|
}
|
||||||
|
}
|
200
src/main/java/themixray/repeating/mod/render/RenderHelper.java
Normal file
200
src/main/java/themixray/repeating/mod/render/RenderHelper.java
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package themixray.repeating.mod.render;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import themixray.repeating.mod.render.buffer.WorldBuffer;
|
||||||
|
import themixray.repeating.mod.render.shader.ShaderManager;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class RenderHelper {
|
||||||
|
public WorldBuffer startLines(WorldRenderContext context) {
|
||||||
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
return new WorldBuffer(GL_LINES, ShaderManager.getPositionColorShader(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endLines(WorldBuffer buffer) {
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDepthMask(false);
|
||||||
|
buffer.draw();
|
||||||
|
glDepthMask(true);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) {
|
||||||
|
buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||||
|
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldBuffer startTri(WorldRenderContext context) {
|
||||||
|
return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endTri(WorldBuffer buffer) {
|
||||||
|
//glDepthRange(0, 0.7);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDepthMask(false);
|
||||||
|
buffer.draw();
|
||||||
|
glDepthMask(true);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDepthRange(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) {
|
||||||
|
buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||||
|
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||||
|
buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawRectFromTri(WorldBuffer buffer,
|
||||||
|
float x1, float y1, float z1,
|
||||||
|
float x2, float y2, float z2,
|
||||||
|
float x3, float y3, float z3,
|
||||||
|
float x4, float y4, float z4,
|
||||||
|
Color color) {
|
||||||
|
drawTri(buffer,
|
||||||
|
x1, y1, z1,
|
||||||
|
x2, y2, z2,
|
||||||
|
x3, y3, z3,
|
||||||
|
color);
|
||||||
|
drawTri(buffer,
|
||||||
|
x3, y3, z3,
|
||||||
|
x4, y4, z4,
|
||||||
|
x1, y1, z1,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawRectFromLines(WorldBuffer buffer,
|
||||||
|
float x1, float y1, float z1,
|
||||||
|
float x2, float y2, float z2,
|
||||||
|
float x3, float y3, float z3,
|
||||||
|
float x4, float y4, float z4,
|
||||||
|
Color color) {
|
||||||
|
drawLine(buffer,
|
||||||
|
x1, y1, z1,
|
||||||
|
x2, y2, z2,
|
||||||
|
color);
|
||||||
|
drawLine(buffer,
|
||||||
|
x2, y2, z2,
|
||||||
|
x3, y3, z3,
|
||||||
|
color);
|
||||||
|
drawLine(buffer,
|
||||||
|
x3, y3, z3,
|
||||||
|
x4, y4, z4,
|
||||||
|
color);
|
||||||
|
drawLine(buffer,
|
||||||
|
x4, y4, z4,
|
||||||
|
x1, y1, z1,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawBoxFromTri(WorldBuffer buffer,
|
||||||
|
float x1, float y1, float z1,
|
||||||
|
float x2, float y2, float z2,
|
||||||
|
Color color) {
|
||||||
|
float[][] v = new float[][]{
|
||||||
|
new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)},
|
||||||
|
new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}};
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromTri(buffer,
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawBoxFromLines(WorldBuffer buffer,
|
||||||
|
float x1, float y1, float z1,
|
||||||
|
float x2, float y2, float z2,
|
||||||
|
Color color) {
|
||||||
|
float[][] v = new float[][]{
|
||||||
|
new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)},
|
||||||
|
new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}};
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[0][0], v[0][1], v[0][2],
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[0][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[0][0], v[1][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[0][0], v[1][1], v[1][2],
|
||||||
|
color);
|
||||||
|
|
||||||
|
drawRectFromLines(buffer,
|
||||||
|
v[1][0], v[0][1], v[0][2],
|
||||||
|
v[1][0], v[0][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[1][2],
|
||||||
|
v[1][0], v[1][1], v[0][2],
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package themixray.repeating.mod.render;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import themixray.repeating.mod.render.buffer.BufferManager;
|
||||||
|
import themixray.repeating.mod.render.shader.ShaderManager;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class RenderSystem {
|
||||||
|
public void init() {
|
||||||
|
BufferManager.init();
|
||||||
|
ShaderManager.init();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package themixray.repeating.mod.render.buffer;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class BufferManager {
|
||||||
|
private int vao;
|
||||||
|
private int vbo;
|
||||||
|
|
||||||
|
private int prevVao;
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
|
||||||
|
vao = glGenVertexArrays();
|
||||||
|
vbo = glGenBuffers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bindBuffer() {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbindBuffer() {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeBuffer(FloatBuffer buffer) {
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(int drawMode, int verts) {
|
||||||
|
glDrawArrays(drawMode, 0, verts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind() {
|
||||||
|
prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING);
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbind() {
|
||||||
|
glBindVertexArray(prevVao);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package themixray.repeating.mod.render.buffer;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class Vertex {
|
||||||
|
@Getter
|
||||||
|
private float x;
|
||||||
|
@Getter
|
||||||
|
private float y;
|
||||||
|
@Getter
|
||||||
|
private float z;
|
||||||
|
@Getter
|
||||||
|
private float r;
|
||||||
|
@Getter
|
||||||
|
private float g;
|
||||||
|
@Getter
|
||||||
|
private float b;
|
||||||
|
@Getter
|
||||||
|
private float a;
|
||||||
|
|
||||||
|
public Vertex(float x, float y, float z, float r, float g, float b, float a) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package themixray.repeating.mod.render.buffer;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import themixray.repeating.mod.render.shader.Shader;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
public class WorldBuffer {
|
||||||
|
private final List<Vertex> vertices = new ArrayList<>();
|
||||||
|
private final int drawMode;
|
||||||
|
private final Shader shader;
|
||||||
|
private FloatBuffer projectionMatrix;
|
||||||
|
private final Vec3d cameraPos;
|
||||||
|
|
||||||
|
public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) {
|
||||||
|
this.drawMode = drawMode;
|
||||||
|
this.shader = shader;
|
||||||
|
this.cameraPos = worldRenderContext.camera().getPos();
|
||||||
|
makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void vert(float x, float y, float z, float r, float g, float b, float a) {
|
||||||
|
vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw() {
|
||||||
|
BufferManager.bind();
|
||||||
|
BufferManager.bindBuffer();
|
||||||
|
|
||||||
|
BufferManager.writeBuffer(getBuffer());
|
||||||
|
applyProjectionMatrix();
|
||||||
|
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, vertices.size() * 3 * 4L);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
BufferManager.unbindBuffer();
|
||||||
|
|
||||||
|
shader.bind();
|
||||||
|
BufferManager.draw(drawMode, this.vertices.size());
|
||||||
|
shader.unbind();
|
||||||
|
|
||||||
|
BufferManager.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FloatBuffer getBuffer() {
|
||||||
|
FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7);
|
||||||
|
ArrayList<Float> floats = new ArrayList<>();
|
||||||
|
for (Vertex vertex : vertices) {
|
||||||
|
floats.add(vertex.getX());
|
||||||
|
floats.add(vertex.getY());
|
||||||
|
floats.add(vertex.getZ());
|
||||||
|
}
|
||||||
|
for (Vertex vertex : vertices) {
|
||||||
|
floats.add(vertex.getR());
|
||||||
|
floats.add(vertex.getG());
|
||||||
|
floats.add(vertex.getB());
|
||||||
|
floats.add(vertex.getA());
|
||||||
|
}
|
||||||
|
Float[] floatArray = new Float[floats.size()];
|
||||||
|
floats.toArray(floatArray);
|
||||||
|
floatBuffer.put(ArrayUtils.toPrimitive(floatArray));
|
||||||
|
return floatBuffer.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) {
|
||||||
|
this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyProjectionMatrix() {
|
||||||
|
shader.uniformMatrix4f("u_projection", projectionMatrix);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package themixray.repeating.mod.render.shader;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
public class Shader {
|
||||||
|
@Getter
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
|
||||||
|
public Shader(String name) {
|
||||||
|
int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX);
|
||||||
|
int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT);
|
||||||
|
this.id = glCreateProgram();
|
||||||
|
glAttachShader(id, v);
|
||||||
|
glAttachShader(id, f);
|
||||||
|
glLinkProgram(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind() {
|
||||||
|
glUseProgram(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbind() {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uniformMatrix4f(String name, FloatBuffer matrix) {
|
||||||
|
bind();
|
||||||
|
glUniformMatrix4fv(glGetUniformLocation(id, name), false, matrix);
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uniformValue2f(String name, float value1, float value2) {
|
||||||
|
bind();
|
||||||
|
glUniform2f(glGetUniformLocation(id, name), value1, value2);
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package themixray.repeating.mod.render.shader;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
|
import com.mojang.blaze3d.platform.TextureUtil;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gl.GlImportProcessor;
|
||||||
|
import net.minecraft.resource.Resource;
|
||||||
|
import net.minecraft.resource.ResourceFactory;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class ShaderManager {
|
||||||
|
@Getter
|
||||||
|
private Shader positionColorShader;
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadShaders() {
|
||||||
|
positionColorShader = new Shader("position_color");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int loadShaderProgram(String name, ShaderType type) {
|
||||||
|
try {
|
||||||
|
boolean file_present = true;
|
||||||
|
ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager();
|
||||||
|
Optional<Resource> resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension));
|
||||||
|
int i = glCreateShader(type.glType);
|
||||||
|
if (resource.isPresent()) {
|
||||||
|
GlStateManager.glShaderSource(i, new GlImportProcessor() {
|
||||||
|
@SneakyThrows
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String loadImport(boolean inline, String name) {
|
||||||
|
return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}.readSource(readResourceAsString(resource.get().getInputStream())));
|
||||||
|
} else file_present = false;
|
||||||
|
glCompileShader(i);
|
||||||
|
if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) {
|
||||||
|
String shaderInfo = StringUtils.trim(glGetShaderInfoLog(i, 32768));
|
||||||
|
throw new IOException("Couldn't compile " + type.name + " program (" + name + ") : " + shaderInfo);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readResourceAsString(InputStream inputStream) {
|
||||||
|
ByteBuffer byteBuffer = null;
|
||||||
|
try {
|
||||||
|
byteBuffer = TextureUtil.readResource(inputStream);
|
||||||
|
int i = byteBuffer.position();
|
||||||
|
byteBuffer.rewind();
|
||||||
|
return MemoryUtil.memASCII(byteBuffer, i);
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
} finally {
|
||||||
|
if (byteBuffer != null) {
|
||||||
|
MemoryUtil.memFree(byteBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ShaderType {
|
||||||
|
VERTEX("vertex", ".vsh", GL_VERTEX_SHADER),
|
||||||
|
FRAGMENT("fragment", ".fsh", GL_FRAGMENT_SHADER);
|
||||||
|
private final String name;
|
||||||
|
private final String fileExtension;
|
||||||
|
private final int glType;
|
||||||
|
|
||||||
|
ShaderType(String name, String fileExtension, int glType) {
|
||||||
|
this.name = name;
|
||||||
|
this.fileExtension = fileExtension;
|
||||||
|
this.glType = glType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/main/resources/assets/minecraft/font/default.json
Normal file
17
src/main/resources/assets/minecraft/font/default.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"providers": [
|
||||||
|
{
|
||||||
|
"file":"repeating-mod:ui/no-loop.png",
|
||||||
|
"chars":["\ueffe"],
|
||||||
|
"height":16,
|
||||||
|
"ascent":12,
|
||||||
|
"type":"bitmap"
|
||||||
|
},{
|
||||||
|
"file":"repeating-mod:ui/loop.png",
|
||||||
|
"chars":["\uefff"],
|
||||||
|
"height":16,
|
||||||
|
"ascent":12,
|
||||||
|
"type":"bitmap"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
src/main/resources/assets/renderer/shader/position_color.fsh
Normal file
12
src/main/resources/assets/renderer/shader/position_color.fsh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
out vec4 fragmentColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (vertexColor.a == 0.0f) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
fragmentColor = vertexColor;
|
||||||
|
}
|
13
src/main/resources/assets/renderer/shader/position_color.vsh
Normal file
13
src/main/resources/assets/renderer/shader/position_color.vsh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 i_pos;
|
||||||
|
layout (location = 1) in vec4 i_color;
|
||||||
|
|
||||||
|
uniform mat4 u_projection;
|
||||||
|
|
||||||
|
out vec4 vertexColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_projection * vec4(i_pos, 1.0f);
|
||||||
|
vertexColor = i_color;
|
||||||
|
}
|
@ -4,20 +4,21 @@
|
|||||||
"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.record": "record",
|
"text.repeating-mod.start_record": "Start record",
|
||||||
"text.repeating-mod.replay": "replay",
|
"text.repeating-mod.stop_record": "Stop record",
|
||||||
"text.repeating-mod.start": "Start",
|
"text.repeating-mod.start_replay": "Start replay",
|
||||||
"text.repeating-mod.stop": "Stop",
|
"text.repeating-mod.stop_replay": "Stop replay",
|
||||||
|
"text.repeating-mod.record_tooltip": "Start/stop recording all activities",
|
||||||
|
"text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions",
|
||||||
|
"text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay",
|
||||||
"text.repeating-mod.export": "Export record",
|
"text.repeating-mod.export": "Export record",
|
||||||
"text.repeating-mod.import": "Import record",
|
"text.repeating-mod.import": "Import record",
|
||||||
"text.repeating-mod.basic": "Basic mode",
|
"text.repeating-mod.export_tooltip": "Exporting a recording to a file",
|
||||||
"text.repeating-mod.parkour": "Parkour mode",
|
"text.repeating-mod.import_tooltip": "Importing an entry from the import.txt file",
|
||||||
"text.repeating-mod.settings": "Settings",
|
|
||||||
"text.repeating-mod.dev": "In development...",
|
"text.repeating-mod.dev": "In development...",
|
||||||
"text.repeating-mod.block_limit": "Block limit: %s",
|
"text.repeating-mod.nan_pos_delay": "No pos timer",
|
||||||
"text.repeating-mod.block_limit_tooltip": "Two recording events will\nbe summed up if the\ndistance between them is\nless than the limit.",
|
"text.repeating-mod.pos_delay": "Pos timer: %s ticks",
|
||||||
"text.repeating-mod.time_limit": "Time limit: %s ms",
|
"text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)",
|
||||||
"text.repeating-mod.time_limit_tooltip": "Two recording events will\nbe summed up if the time\nbetween them is less than\nthe limit.",
|
|
||||||
|
|
||||||
"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",
|
||||||
|
@ -4,20 +4,21 @@
|
|||||||
"key.repeating-mod.toggle_record": "Вкл/выкл запись",
|
"key.repeating-mod.toggle_record": "Вкл/выкл запись",
|
||||||
|
|
||||||
"text.repeating-mod.name": "Репитинг Мод",
|
"text.repeating-mod.name": "Репитинг Мод",
|
||||||
"text.repeating-mod.record": "запись",
|
"text.repeating-mod.start_record": "Начать запись",
|
||||||
"text.repeating-mod.replay": "повтор",
|
"text.repeating-mod.stop_record": "Остановить запись",
|
||||||
"text.repeating-mod.start": "Начать",
|
"text.repeating-mod.start_replay": "Начать повтор",
|
||||||
"text.repeating-mod.stop": "Остановить",
|
"text.repeating-mod.stop_replay": "Остановить повтор",
|
||||||
|
"text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий",
|
||||||
|
"text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий",
|
||||||
|
"text.repeating-mod.loop_tooltip": "Вкл/выкл повтор повтора записанных действий",
|
||||||
"text.repeating-mod.export": "Экспорт записи",
|
"text.repeating-mod.export": "Экспорт записи",
|
||||||
"text.repeating-mod.import": "Импорт записи",
|
"text.repeating-mod.import": "Импорт записи",
|
||||||
"text.repeating-mod.basic": "Обычный режим",
|
"text.repeating-mod.export_tooltip": "Экспорт записи в файл",
|
||||||
"text.repeating-mod.parkour": "Режим паркура",
|
"text.repeating-mod.import_tooltip": "Импорт записи из файла import.txt",
|
||||||
"text.repeating-mod.settings": "Настройки",
|
|
||||||
"text.repeating-mod.dev": "В разработке...",
|
"text.repeating-mod.dev": "В разработке...",
|
||||||
"text.repeating-mod.block_limit": "Лимит блоков: %s",
|
"text.repeating-mod.nan_pos_delay": "Таймера позиции нету",
|
||||||
"text.repeating-mod.block_limit_tooltip": "Два ивента записи будут\nсуммироваться, если\nрасстояние между ними\nменьше лимита.",
|
"text.repeating-mod.pos_delay": "Таймер позиции: %s тиков",
|
||||||
"text.repeating-mod.time_limit": "Лимит времени: %s мс",
|
"text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)",
|
||||||
"text.repeating-mod.time_limit_tooltip": "Два ивента записи будут\nсуммироваться, если время\nмежду ними меньше лимита.",
|
|
||||||
|
|
||||||
"message.repeating-mod.replay_start": "Повтор начат",
|
"message.repeating-mod.replay_start": "Повтор начат",
|
||||||
"message.repeating-mod.replay_stop": "Повтор закончен",
|
"message.repeating-mod.replay_stop": "Повтор закончен",
|
||||||
|
BIN
src/main/resources/assets/repeating-mod/textures/ui/loop.png
Normal file
BIN
src/main/resources/assets/repeating-mod/textures/ui/loop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 B |
BIN
src/main/resources/assets/repeating-mod/textures/ui/no-loop.png
Normal file
BIN
src/main/resources/assets/repeating-mod/textures/ui/no-loop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 B |
@ -4,13 +4,13 @@
|
|||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
|
|
||||||
"name": "Repeating Mod",
|
"name": "Repeating Mod",
|
||||||
"description": "Mod that repeats your actions. ",
|
"description": "Mod that repeats your recorded actions. ",
|
||||||
"authors": [
|
"authors": [
|
||||||
"TheMixRay"
|
"TheMixRay"
|
||||||
],
|
],
|
||||||
"contact": {
|
"contact": {
|
||||||
"homepage": "https://fabricmc.net/",
|
"homepage": "https://modrinth.com/mod/repeating-mod",
|
||||||
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
"sources": "https://github.com/MeexReay/repeating-mod"
|
||||||
},
|
},
|
||||||
|
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.14.14",
|
"fabricloader": ">=0.14.14",
|
||||||
"fabric-api": "*",
|
"fabric-api": "*",
|
||||||
"minecraft": "~1.19.3",
|
"minecraft": ">=1.20",
|
||||||
"java": ">=17"
|
"java": ">=17"
|
||||||
},
|
},
|
||||||
"suggests": {
|
"suggests": {
|
||||||
|
BIN
src/main/resources/icon.png
Normal file
BIN
src/main/resources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -7,7 +7,10 @@
|
|||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"MovementMixin",
|
"MovementMixin",
|
||||||
"InputMixin"
|
"InputMixin",
|
||||||
|
"RendererMixin",
|
||||||
|
"EntityMixin",
|
||||||
|
"ClientMixin"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
Loading…
Reference in New Issue
Block a user